From: Christian Marillat Date: Mon, 10 Aug 2026 18:36:30 +0000 (+0200) Subject: Import libtorrent-rasterbar_2.0.14-2.debian.tar.xz X-Git-Tag: archive/raspbian/2.0.14-2+rpi1^2~2^2 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=09aed964207a402d2bae0f2f08c96110ce9a79cb;p=libtorrent-rasterbar.git Import libtorrent-rasterbar_2.0.14-2.debian.tar.xz [dgit import tarball libtorrent-rasterbar 2.0.14-2 libtorrent-rasterbar_2.0.14-2.debian.tar.xz] --- 09aed964207a402d2bae0f2f08c96110ce9a79cb diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..845003c --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,9 @@ +image: registry.gitlab.com/eighthave/ci-image-git-buildpackage:latest + +build: + artifacts: + paths: + - "*.deb" + expire_in: 1 day + script: + - gitlab-ci-git-buildpackage-all diff --git a/README.Debian b/README.Debian new file mode 100644 index 0000000..a654481 --- /dev/null +++ b/README.Debian @@ -0,0 +1,74 @@ +libtorrent-rasterbar for Debian +------------------------------- + +* libtorrent-rasterbar 0.14.2-2 + +Revert build dependencies to boost1.37 only. + + -- Cristian Greco Wed, 11 Mar 2009 16:27:20 +0100 + +* libtorrent-rasterbar 0.14.2-1 + +Enabled building against boost1.37 due to the boost library transition: build +dependencies will be updated each time a new version of boost get uploaded in +Debian (this will happen likely two times a year in the future). +Building against boost1.34.1 is still supported for backport purposes. + + -- Cristian Greco Fri, 27 Feb 2009 07:46:38 +0100 + + +* libtorrent-rasterbar 0.14.1-1 + +While version 0.14 was built against boost1.36, it seems like this release of +the boost libraries has been deleted from Debian and maintainers encourage use +of the next stable release boost1.37. +Anyhow, the best choice actually is to build 0.14.1 against boost1.34.1 because +it is a widely used and well tested release, and on the other hand maybe +libtorrent-rasterbar is not yet ready to support boost1.37. + + -- Cristian Greco Mon, 15 Dec 2008 15:34:17 +0100 + + +* libtorrent-rasterbar 0.14-1 + +- Python bindings for libtorrent-rasterbar +Added a patch (merged upstream, will be available in 0.14.1) to build python +bindings using configure and make. The name of the python module (and package) +is 'python-libtorrent' (as requested by upstream). + +- Lintian Info tag 'no-symbols-control-file' +Maintaining a symbols control file for that library is more a cost than a +benefit. In fact, libtorrent-rasterbar exports a lot of symbols (quite big +symbols files, ~600Kb) and the C++ ABI mangling requires one to maintain a +different control file for each architecture. Moreover this library has a few +reverse dependencies (less than 10 clients, much of them not yet packaged for +Debian), so it wouldn't be difficult to maintain them. + + -- Cristian Greco Sun, 23 Nov 2008 18:55:24 +0100 + + +* libtorrent-rasterbar 0.13.1-2 + +Building a debug version (--enable-debug) of libtorrent-rasterbar seems to +cause some clients to crash (such as qbittorrent, btg - qbittorrent needs a +special option in order to build against a debug version), and actually most +clients upstream developers seems to prefer a release build of the library. + +Then, now libtorrent-rasterbar builds a release version (--disable-debug). + +NOTE for maintainers of all clients which depend on libtorrent-rasterbar: +please build-depends on this exact revision of the package in your +debian/control file: + Build-Depends: libtorrent-rasterbar-dev (>= 0.13.1-2) + + -- Cristian Greco Wed, 23 Jul 2008 11:18:31 +0200 + + +* libtorrent-rasterbar 0.13.1-1 + +Actually, libtorrent-rasterbar has to be built against boost-1.34 and libasio +instead of boost-1.35 (which includes libasio and conflicts with version 1.34); +this is for compatibility with most bittorrent clients which depend also on +boost-1.34. + + -- Cristian Greco Wed, 02 Jul 2008 10:46:21 +0200 diff --git a/changelog b/changelog new file mode 100644 index 0000000..333bfa6 --- /dev/null +++ b/changelog @@ -0,0 +1,841 @@ +libtorrent-rasterbar (2.0.14-2) unstable; urgency=medium + + * Forgot to install git submodules. + + -- Christian Marillat Mon, 10 Aug 2026 20:36:30 +0200 + +libtorrent-rasterbar (2.0.14-1) unstable; urgency=medium + + * New upstream release. + + -- Christian Marillat Mon, 10 Aug 2026 17:27:22 +0200 + +libtorrent-rasterbar (2.0.13-1) unstable; urgency=medium + + * New upstream release. + * openssl 4.0 support has been added (Closes: #1138465) + * Fix 'Fails to build source after successful build' (Closes: #1048692) + + -- Christian Marillat Tue, 09 Jun 2026 00:48:34 +0200 + +libtorrent-rasterbar (2.0.12-1) unstable; urgency=medium + + * New upstream release. + * Update old fsf address in copyright. + + -- Christian Marillat Sat, 14 Mar 2026 11:32:56 +0100 + +libtorrent-rasterbar (2.0.11-3) unstable; urgency=medium + + * Add libboost-dev as dependency for libtorrent-rasterbar-dev + (Closes: #1127426) + + -- Christian Marillat Mon, 09 Feb 2026 00:57:45 +0100 + +libtorrent-rasterbar (2.0.11-2) unstable; urgency=medium + + * Remove libboost-system-dev from Build-Depends and from + libtorrent-rasterbar-dev Dependency (Closes: #1127230) + + -- Christian Marillat Fri, 06 Feb 2026 18:47:00 +0100 + +libtorrent-rasterbar (2.0.11-1) unstable; urgency=medium + + * New upstream release. + * Should Build-Depends on a versioned libboost-system-dev '>= 1.81' + + -- Christian Marillat Tue, 28 Jan 2025 15:33:12 +0100 + +libtorrent-rasterbar (2.0.10-1.1) unstable; urgency=medium + + * Non-maintainer upload. + * Rename libraries for 64-bit time_t transition. Closes: #1062684 + + -- Graham Inggs Wed, 28 Feb 2024 20:35:15 +0000 + +libtorrent-rasterbar (2.0.10-1) unstable; urgency=medium + + * New upstream release. + * debian/control Replaces pkg-config by pkgconf + + -- Christian Marillat Mon, 19 Feb 2024 14:34:35 +0100 + +libtorrent-rasterbar (2.0.9-2) unstable; urgency=medium + + * Fix 'Fails to build source after successful build' (Closes: #1048692) + + -- Christian Marillat Mon, 14 Aug 2023 07:25:12 +0200 + +libtorrent-rasterbar (2.0.9-1) unstable; urgency=medium + + * New upstream release. + + -- Christian Marillat Tue, 30 May 2023 07:52:47 +0200 + +libtorrent-rasterbar (2.0.8-1) unstable; urgency=medium + + * New upstream release. + + -- Christian Marillat Sun, 23 Oct 2022 17:10:21 +0200 + +libtorrent-rasterbar (2.0.7-1) unstable; urgency=medium + + * New upstream release. + * Removed #1014795 hack, fixed upstream. + + -- Christian Marillat Sun, 17 Jul 2022 23:55:43 +0200 + +libtorrent-rasterbar (2.0.6-5) unstable; urgency=medium + + * Restore hack to fix #1014795 + + -- Christian Marillat Tue, 12 Jul 2022 17:08:26 +0200 + +libtorrent-rasterbar (2.0.6-4) unstable; urgency=medium + + * Build with latest boost1.74 (Closes: #1013931) + + -- Christian Marillat Mon, 04 Jul 2022 12:40:15 +0200 + +libtorrent-rasterbar (2.0.6-3) unstable; urgency=medium + + * debian/rules Add hack to fix broken pkg-config file (Closes: #1009875) + + -- Christian Marillat Tue, 19 Apr 2022 20:41:48 +0200 + +libtorrent-rasterbar (2.0.6-2) unstable; urgency=medium + + * debian/rules don't call dh_auto_install-indep + + -- Christian Marillat Sun, 17 Apr 2022 14:31:55 +0200 + +libtorrent-rasterbar (2.0.6-1) unstable; urgency=medium + + * New upstream release. + * Add -indep rules to build the -doc package. + + -- Christian Marillat Sun, 17 Apr 2022 09:29:18 +0200 + +libtorrent-rasterbar (2.0.5-6) unstable; urgency=medium + + * Remove unneeded ConvertUTF.cpp/.h in debian/copyright (CLoses: #1003423) + + -- Christian Marillat Sat, 26 Feb 2022 15:39:40 +0100 + +libtorrent-rasterbar (2.0.5-5) unstable; urgency=medium + + * Well, really fix bug #1004702 + + -- Christian Marillat Tue, 01 Feb 2022 15:13:18 +0100 + +libtorrent-rasterbar (2.0.5-4) unstable; urgency=medium + + * debian/control replace python3-all-dev by python3-dev (Closes: #1004702) + + -- Christian Marillat Tue, 01 Feb 2022 01:09:11 +0100 + +libtorrent-rasterbar (2.0.5-3) unstable; urgency=medium + + * Build for unstable. + + -- Christian Marillat Sun, 30 Jan 2022 19:29:05 +0100 + +libtorrent-rasterbar (2.0.5-2) experimental; urgency=medium + + * debian/copyright add simulation/libsimulator/* + + -- Christian Marillat Sun, 23 Jan 2022 13:10:13 +0100 + +libtorrent-rasterbar (2.0.5-1) unstable; urgency=medium + + * New maintainer (Closes: #995076) + * New upstream release (CLoses: #969780) + * Fix python issues (Closes: #992575, #856600) + * Fix security issues (Closes: #987120) + * Fix vlc-plugin-bittorrent crash (Closes: #916399) + * Fix deluge crash (Closes: #999422) + + -- Christian Marillat Fri, 17 Dec 2021 11:21:19 +0100 + +libtorrent-rasterbar (2.0.4-1) experimental; urgency=medium + + * New maintainer (Closes: #995076) + * Bump Standards-Version to 4.6.0 (No change) + * Upgrade to debhelper-compat 13 and dh-sequence-python3 + * Remove unneeded libtorrent-rasterbar-dbg package. + * debian/rules Don't use 'dh_makeshlibs -V' now the default with debhelper 13 + * Build with cmake and ninja + + -- Christian Marillat Thu, 30 Sep 2021 14:53:12 +0200 + +libtorrent-rasterbar (1.2.14-1) unstable; urgency=medium + + [ Debian Janitor ] + * Apply multi-arch hints. + + libtorrent-rasterbar-dbg, libtorrent-rasterbar-dev, + libtorrent-rasterbar10: Add Multi-Arch: same. + + libtorrent-rasterbar-doc: Add Multi-Arch: foreign. + + [ Andrew Starr-Bochicchio ] + * New upstream release. + * Drop debian/patches/0001-python-move-storage.patch: + - Applied upstream. + * Stop building python3-libtorrent-dbg (Closes: #994306). + * debian/control: Add build depends on python3-setuptools and + libboost-tools-dev. + + -- Andrew Starr-Bochicchio Sat, 25 Sep 2021 11:40:11 -0400 + +libtorrent-rasterbar (1.2.9-0.3) unstable; urgency=medium + + * Non-maintainer upload. + * Added d/patches/ README suggesting patch ordering and naming. + * Added patch from upstream to make python move_storage() match + C++ signature (Closes: #987306). + + -- Petter Reinholdtsen Fri, 30 Apr 2021 07:51:42 +0200 + +libtorrent-rasterbar (1.2.9-0.2) unstable; urgency=medium + + * Non-maintainer upload. + * Fix building Python bindings with -std=c++14 (Closes: #975953), idea from + Holger Hoffstätte, https://bugs.gentoo.org/739654. + + -- Andrey Rahmatullin Sat, 05 Dec 2020 16:49:55 +0500 + +libtorrent-rasterbar (1.2.9-0.1) unstable; urgency=medium + + * Non-maintainer upload. + + [ Andrew Starr-Bochicchio ] + * New upstream release. + - Removes src/ConvertUTF.cpp (Closes: #956285). + * debian/copyright: Address issues found in copyright + review. + * debian/control: + - Remove duplicated python3-docutils build-dep. + - Drop python build-dep (Closes: #967169, #936927). + * debian/rules: + - Prevent call to pyversions by using pybuild (#962090). + - Pass --std=c++14 + + -- Jonas Smedegaard Wed, 25 Nov 2020 09:34:56 +0100 + +libtorrent-rasterbar (1.2.5-1.2) unstable; urgency=medium + + * Non-maintainer upload + * Link against libatomic on riscv64 as well + + -- Graham Inggs Tue, 23 Jun 2020 18:11:28 +0000 + +libtorrent-rasterbar (1.2.5-1.1) unstable; urgency=high + + * Non-maintainer upload. + * Link against libatomic on armel/mipsel/m68k/powerpc/sh4. + (Closes: #958958) + * Build depend on python, which reopens #936927 but closes: #962090. + + -- Adrian Bunk Wed, 10 Jun 2020 17:37:20 +0300 + +libtorrent-rasterbar (1.2.5-1) unstable; urgency=medium + + * New upstream release (Closes: #952447). + * debian/control: + - Depend on python3-docutils (Closes: #936927). + - Bump Standards-Version to 4.5.0. + * New package libtorrent-rasterbar10, bump library soname. + + -- Andrew Starr-Bochicchio Mon, 09 Mar 2020 20:41:03 -0400 + +libtorrent-rasterbar (1.1.13-1.1) unstable; urgency=medium + + * Non-maintainer upload. + * Stop building the Python2 packages. Closes: #936927. + * Fix installation of the python3.8 extension. Closes: #945473. + + -- Matthias Klose Mon, 25 Nov 2019 17:42:44 +0100 + +libtorrent-rasterbar (1.1.13-1) unstable; urgency=medium + + * New upstream version 1.1.13 + * Team upload. + + -- Ondřej Surý Thu, 11 Jul 2019 09:18:04 +0200 + +libtorrent-rasterbar (1.1.11-2) unstable; urgency=medium + + * d/rules: Use boost-python >=1.67 soname suffix to fix + Python 3 linking (LP: #1800264). + + -- Calum Lind Sat, 08 Dec 2018 21:49:08 -0500 + +libtorrent-rasterbar (1.1.11-1) unstable; urgency=medium + + [ Ondřej Nový ] + * d/control: Remove ancient X-Python-Version field + * d/control: Remove ancient X-Python3-Version field + + [ Ondřej Surý ] + * New upstream version 1.1.11 + * Team upload. + + -- Ondřej Surý Sun, 02 Dec 2018 09:38:41 +0000 + +libtorrent-rasterbar (1.1.9-1) unstable; urgency=medium + + [ Unit 193 ] + * New upstream release (Closes: #900975). + - d/p/python-dbg-build-fix.patch: Drop, wasn't in series, fixed upstream. + - d/p/fix-html-docs.patch: Refresh and extend patch. + * d/compat, d/control: Bump dh compat to 11. + * d/control: + - Add build-depend on dh-python. + - Drop versioned build-depend on dpkg-dev. + - Drop Pre-Depends on dpkg (Was for xz support.) + - Update homepage to https://libtorrent.org/ (Closes: #900946). + - Update Vcs-* for Salsa. + - Fix python3-libtorrent-dbg depend: + python-libtorrent → python3-libtorrent (Closes: #906078). + * d/control, d/copyright, d/rules: + Upstream dropped geoip support, drop references. + * d/copyright: + - Update ConvertUTF.(cpp,h) license. + - Update 'format' field to use https. + * d/rules: + - Drop --parallel, autoreconf, and xz compression. These are now default. + * Bump Standards-Version to 4.1.4. + * Remove trailing whitespaces under debian/* + + [ Andrew Starr-Bochicchio ] + * New upstream release. + + -- Andrew Starr-Bochicchio Sat, 22 Sep 2018 20:54:52 -0400 + +libtorrent-rasterbar (1.1.5-1) unstable; urgency=medium + + * New upstream release. + * Bump Standards-Version 4.1.2, no changes. + + -- Andrew Starr-Bochicchio Mon, 25 Dec 2017 14:29:03 -0500 + +libtorrent-rasterbar (1.1.4-1) unstable; urgency=medium + + * New upstream release (Closes: #857239). + - Fix file rename issue with name prefix matching + torrent name (Closes: #874136). + - Fix ABI compatibility issue introduced with + preformatted entry type (Closes: #842538). + * debian/libtorrent-rasterbar-doc.install: + - Adjust for new release. + * debian/control: + - All priority 'extra' packages are now 'optional.' + - Bump Standards-Version to 4.1.0. + - Drop un-needed build dep on dh-autoreconf. + + -- Andrew Starr-Bochicchio Sun, 03 Sep 2017 13:28:04 -0400 + +libtorrent-rasterbar (1.1.1-1) unstable; urgency=medium + + * New upstream release. + * Drop remove-msse4.2.patch, fix-get-ip-filter.patch, + 1.1.0-python-fixes.patch, and fix-invalid-input-crash.patch. + All applied upstream. + * Bump debian/compat to 10. + * Adjust install files for multi-arch. + + -- Andrew Starr-Bochicchio Sat, 29 Oct 2016 16:39:13 -0400 + +libtorrent-rasterbar (1.1.0-3) unstable; urgency=medium + + * fix-get-ip-filter.patch: Backport upstream fix for + crash in session::get_ip_filter (Closes: #834630). + * 1.1.0-python-fixes.patch: Backport a number of upstream + commits with fixes for the Python bindings. + * fix-invalid-input-crash.patch: Backport upstream commit + fixing crash on invalid input in http_parser. + + -- Andrew Starr-Bochicchio Tue, 23 Aug 2016 20:50:44 -0400 + +libtorrent-rasterbar (1.1.0-2) unstable; urgency=medium + + * Fix builds on arches where the multiarch tuple (i386-*) and + the GNU tuple (i686-*) differ (Closes: #833944). + * remove-msse4.2.patch: Backport upstream commit removing + -msse4.2 commandline option from configure script (Closes: #833944) + + -- Andrew Starr-Bochicchio Wed, 10 Aug 2016 20:23:29 -0400 + +libtorrent-rasterbar (1.1.0-1) unstable; urgency=medium + + * New upstream release. + - Fixes CVE-2016-5301, crash while parsing invalid + chunked HTTP or UPnP response (Closes: #826380). + * New package libtorrent-rasterbar9, bump library soname. + * Drop python-clean-without-bjam.patch, applied upstream. + * Drop missing_rst_file.patch, applied upstream. + * Build depend on libboost-chrono-dev and libboost-random-dev. + * python-dbg-build-fix.patch: Fix building bindings with python-dbg. + Thanks to Calum Lind. + * Bump Standards-Version to 3.9.8, no changes. + * Build with dh_autoreconf. + + -- Andrew Starr-Bochicchio Mon, 08 Aug 2016 23:38:37 -0400 + +libtorrent-rasterbar (1.0.7-1) unstable; urgency=medium + + * New upstream release (Closes: #806396). + * Update Source link in debian/copyright. + * debian/patches/missing_rst_file.patch: Add file missing + from upstream distribution. + + -- Andrew Starr-Bochicchio Fri, 27 Nov 2015 12:41:42 -0500 + +libtorrent-rasterbar (1.0.6-2) unstable; urgency=medium + + * Upload to unstable (Closes: #791176). + + -- Andrew Starr-Bochicchio Sat, 29 Aug 2015 12:55:09 -0400 + +libtorrent-rasterbar (1.0.6-1) experimental; urgency=medium + + * New upstream release (Closes: #785676). + - Bump library soname. + * Drop fix-python-build-missing-byteshpp.patch, applied upstream. + * python-clean-without-bjam.patch: Only use bjam when '--bjam' + is passed explicitly. + * Bump Standards-Version to 3.9.6, no changes. + * Point debian/watch at GitHub. + + -- Andrew Starr-Bochicchio Sat, 01 Aug 2015 18:03:37 -0400 + +libtorrent-rasterbar (0.16.18-1) unstable; urgency=medium + + * New upstream version. + - fix string handling in python3 bindings (Closes: #756503). + * debian/patches: + - fix-python-dbg-build.patch: drop, fixed upstream. + - fix-html-docs.patch: refresh. + - fix-python-build-missing-byteshpp.patch: new, fix missing file + from upstream tarball. + + -- Cristian Greco Sat, 18 Oct 2014 19:47:37 +0200 + +libtorrent-rasterbar (0.16.17-2) unstable; urgency=medium + + * debian/control: switch back to unversioned {build-,}depends + for boost libraries. (Closes: #759387) + + -- Cristian Greco Tue, 02 Sep 2014 22:48:17 +0200 + +libtorrent-rasterbar (0.16.17-1) unstable; urgency=medium + + * New upstream release. + - Don't fall back on wildcard port in UPnP (LP: #1330703). + + -- Andrew Starr-Bochicchio Sun, 22 Jun 2014 11:36:34 -0400 + +libtorrent-rasterbar (0.16.16-1) unstable; urgency=medium + + * New upstream release. + * Update watch file to find new releases of sourceforge. + + -- Andrew Starr-Bochicchio Sat, 07 Jun 2014 15:39:40 -0400 + +libtorrent-rasterbar (0.16.13-1) unstable; urgency=medium + + * New upstream release. + * Add myself as an uploader. + * Bump Standards-Version to 3.9.5, no changes needed. + * Update fix-html-docs.patch so that all docs are built from + source and fix lintian privacy-breach-google-adsense errors. + + -- Andrew Starr-Bochicchio Mon, 24 Feb 2014 14:11:14 -0500 + +libtorrent-rasterbar (0.16.11-2.1) unstable; urgency=medium + + * Non-maintainer upload. + * Explicitly pass --with-boost-system=boost_system to configure + as ax_boost_system.m4 doesn't want to find it now that it + is multiarch. Fixes FTBFS (Closes: #738381). + * Fix build with python3.4 as a supported version, using python-config. + + -- Andrew Starr-Bochicchio Mon, 17 Feb 2014 12:19:53 -0500 + +libtorrent-rasterbar (0.16.11-2) unstable; urgency=low + + * debian/control: libtorrent-rasterbar-dev should depend on + boost-system1.54 (Closes: #726823). + + -- Cristian Greco Sat, 19 Oct 2013 20:26:01 +0200 + +libtorrent-rasterbar (0.16.11-1) unstable; urgency=low + + * Non-maintainer upload. + * New upstream version 0.16.11. + - fixes build with Boost 1.54 (Closes: #724708). + * fix-ftbfs-kfreebsd-iconv.patch: deleted (merged upstream). + * debian/rules: fix linking with boost_python libraries (Closes: #721546). + * debian/control: + - switch to boost1.54-dev | boost-dev deps. + - bump Standards-Version to 3.9.4 (no changes required). + * debian/copyright: + - update in according to Copyright format 1.0 + - remove copyright holders whose code was deleted from source tree + - add missed copyright holders + - update years + + -- Boris Pek Sat, 28 Sep 2013 20:27:56 +0300 + +libtorrent-rasterbar (0.16.10-3.1) unstable; urgency=low + + * Non-maintainer upload. + * Adjust python3-libtorrent.install to fix FTBFS when not built with + python3.2 support (Closes: #718666) + + -- Scott Kitterman Sat, 03 Aug 2013 22:24:08 -0400 + +libtorrent-rasterbar (0.16.10-3) unstable; urgency=low + + * debian/patches: + - fix-ftbfs-kfreebsd-iconv.patch: new patch, fix + ftbfs on kfreebsd, thanks Pino Toscano! (Closes: #712797) + - refresh other patches. + + -- Cristian Greco Thu, 20 Jun 2013 21:18:23 +0200 + +libtorrent-rasterbar (0.16.10-2) unstable; urgency=low + + * Upload to unstable. + - fixes all ftbfs (Closes: #701311, #710624). + * debian/control: switch to conditional build-dep on + boost1.53-dev | boost-dev. + + -- Cristian Greco Mon, 17 Jun 2013 22:56:07 +0200 + +libtorrent-rasterbar (0.16.10-1) experimental; urgency=low + + * New upstream version 0.16.10. + * Build python3 packages (patch by Andrew Starr-Bochicchio + ) (Closes: #708072). + * debian/rules: add explicit configure arg --with-libiconv. + + -- Cristian Greco Sun, 26 May 2013 13:57:13 +0200 + +libtorrent-rasterbar (0.16.9-1) experimental; urgency=low + + * New upstream version 0.16.9. + - new package libtorrent-rasterbar7 (bump up library soname). + - debian/control: drop now useless (build-)deps. + - debian/control: switch to explicit boost1.53 (build-)deps. + - upload to experimental: the library is pretty stable, but will be + uploaded to unstable only after the transition of boost1.53. + + -- Cristian Greco Sun, 12 May 2013 10:48:57 +0200 + +libtorrent-rasterbar (0.15.10-1) unstable; urgency=low + + * New upstream version. + + -- Cristian Greco Tue, 06 Mar 2012 21:27:49 +0100 + +libtorrent-rasterbar (0.15.9-1) unstable; urgency=low + + * New upstream version. + * debian/{control,rules}: compress binary packages with xz and add + Pre-Depends on dpkg (>= 1.15.6~). + * debian/rules: enable default hardening options from dpkg-buildflags. + + -- Cristian Greco Wed, 21 Dec 2011 13:08:22 +0100 + +libtorrent-rasterbar (0.15.8-1) unstable; urgency=low + + * New upstream version. + - fix-webseeds-r5915.patch: drop patch, fixed upstream. + - refreshed other patches. + + -- Cristian Greco Fri, 30 Sep 2011 13:21:21 +0200 + +libtorrent-rasterbar (0.15.7-1) unstable; urgency=low + + * New upstream version. + - fix-webseeds-r5915.patch: new patch, fix a web seeds bug caused + by a wrong merge commit. + * debian/control: + - use my @debian.org address. + - drop DMUA field. + + -- Cristian Greco Fri, 19 Aug 2011 18:47:49 +0200 + +libtorrent-rasterbar (0.15.6-2) unstable; urgency=low + + * debian/control: switch to dh_python2 for the python bindings. + * debian/rules: no need to pass -a to dh_strip and dh_makeshlibs. + * debian/patches: + - fix-html-docs.patch: renamed, added dep3 tags. + - fix-python-dbg-build.patch: new patch, fix wrong configure check + when building with pythonX-dbg. + * debian/{control,rules}: introducing a new python-libtorrent-dbg + package (Closes: #628041). + + -- Cristian Greco Mon, 20 Jun 2011 01:30:51 +0200 + +libtorrent-rasterbar (0.15.6-1) unstable; urgency=low + + * New upstream version. + - upload to unstable. + * Getting rid of the unneeded *.la file in -dev package + (Closes: #622461). + * debian/control: bump Standards-Version to 3.9.2. + * debian/README.Debian: fix a spelling error in an old entry + to make lintian happy. + * debian/rules: simplified a bit. + * debian/{control,rules}: drop "XS-Python-Version: all" and use + different pyversions options instead. + + -- Cristian Greco Tue, 10 May 2011 20:54:50 +0200 + +libtorrent-rasterbar (0.15.5-1) experimental; urgency=low + + * New upstream version. + + -- Cristian Greco Fri, 07 Jan 2011 17:36:29 +0100 + +libtorrent-rasterbar (0.15.4-1) experimental; urgency=low + + * New upstream version. + - upload to experimental: the library is pretty stable, but will be + uploaded to unstable only after the release of Squeeze and the + update of boost-defaults to boost1.44 (or higher). + * debian/control: + - new package libtorrent-rasterbar6 (bump up library soname). + - rework boost build-deps, drop unneeded. + - bump up Standards-Version to 3.9.1. + * debian/rules: + - rework (update) configuration options. + - bump debhelper build-dep to (>= 7.4.10) and enable parallel + build support. + * debian/copyright: + - updated to reflect upstream changes. + - fix copyright-refers-to-deprecated-bsd-license-file. + * debian/patches/100_fix_html_docs.patch: refreshed. + * debian/watch: use a simplified regexp. + + -- Cristian Greco Fri, 10 Dec 2010 00:18:46 +0100 + +libtorrent-rasterbar (0.14.10-2) unstable; urgency=low + + * debian/{control,rules}: build python-libtorrent for all supported + python versions. (Closes: #578078) + * debian/README.source: remove now unneeded file. + + -- Cristian Greco Sat, 17 Apr 2010 00:19:00 +0200 + +libtorrent-rasterbar (0.14.10-1) unstable; urgency=low + + * New upstream version. + + -- Cristian Greco Wed, 24 Mar 2010 18:23:04 +0100 + +libtorrent-rasterbar (0.14.9-1) unstable; urgency=low + + * New upstream version. + * debian/libtorrent-rasterbar-doc.doc-base: fix spelling error. + * debian/patches/100_fix_html_docs.patch: refreshed. + * debian/control: bump up standards-version to 3.8.4 (no changes). + + -- Cristian Greco Sat, 20 Feb 2010 02:00:20 +0100 + +libtorrent-rasterbar (0.14.8-1) unstable; urgency=low + + * New upstream version. + - r4072_fix_broadcast_socket_regression.patch: dropped, included upstream. + * debian/control: add dependency on libgeoip-dev for -dev package. + * debian/watch: point download url to googlecode. + + -- Cristian Greco Thu, 14 Jan 2010 23:25:42 +0100 + +libtorrent-rasterbar (0.14.7-2) unstable; urgency=low + + * r4072_fix_broadcast_socket_regression.patch: new patch, pulled from + upstream svn (r4072). + + -- Cristian Greco Thu, 17 Dec 2009 13:21:49 +0100 + +libtorrent-rasterbar (0.14.7-1) unstable; urgency=low + + * New upstream version. + * debian/control: + - new maintainer email address. + - add DMUA field, thanks to my sponsor Michal Čihař. + - add build-dep on libboost-program-options-dev. + * Convert to 3.0 (quilt) source format. + + -- Cristian Greco Wed, 25 Nov 2009 16:29:30 +0100 + +libtorrent-rasterbar (0.14.6-1) unstable; urgency=low + + * New upstream version. + * debian/{control,rules}: + - build-dep on libgeoip-dev and pkg-config, thus enable building against + geoip system library. (Closes: #544462) + - drop build-dep on autotools-dev and don't overwrite config.{guess,sub} + as upstream now ships up-to-date files. + * debian/rules: drop the hack for boost1.39 as latest revision now + ship again the -mt simlinks. + * 010_r3796_r3797_fix_configure.patch: deleted (merged upstream). + * debian/README.source: fix reference to quilt doc. + + -- Cristian Greco Tue, 22 Sep 2009 12:58:26 +0200 + +libtorrent-rasterbar (0.14.5-1) unstable; urgency=low + + * New upstream version. + - new package libtorrent-rasterbar5 (bump up library soname). + - debian/patches: + + prefix patches with a number. + + 100_fix_html_docs.patch: improve description. + + new patch 010_r3796_r3797_fix_configure.patch (temporary fix). + * debian/control: + - move git package maintenance to collab-maint. + - bump up Standards-Version to 3.8.3 (no changes required). + - (build-)depends on zlib1g-dev instead of libz-dev (which is + a virtual package). + - build-depends on quilt (>= 0.46-7) and simplify debian/rules. + * debian/rules: allow building against boost 1.39 (which contains + libraries with no more -mt suffix) and try to retain compatibility + with older versions for ubuntu backports. + + -- Cristian Greco Tue, 25 Aug 2009 17:29:29 +0200 + +libtorrent-rasterbar (0.14.4-1) unstable; urgency=low + + * New upstream version. + - new package libtorrent-rasterbar4 (bump up library soname). + * debian/control: + - fix Vcs-Browser address; + - build depends on new unversioned boost -dev packages (this will cause + building against boost1.38). (Closes: #531114) + * debian/patches/fix_html_docs.patch: refresh patch as file + docs/libtorrent_plugins.rst was added upstream. + * debian/rules: + - now there is no need to remove the missing doc file generated by + fix_html_docs.patch as it has been fixed upstream; + - backup config.{sub,guess} before overwriting them during build, and + restore during clean. + + -- Cristian Greco Wed, 03 Jun 2009 13:05:21 +0200 + +libtorrent-rasterbar (0.14.3-1) unstable; urgency=low + + * New Upstream Version + - new package libtorrent-rasterbar3 (bump up library soname). + * debian/control: + - add Vcs-* stuff (switch to git-buildpackage) and bump up + Standards-Version to 3.8.1 (no changes required); + - build-depends on debhelper (>= 7.0.50) and use override_dh_command; + - move -dbg package to the new 'debug' section; + - build-depends on autotools-dev and overwrite config.{sub,guess} with a + recent version in debian/rules. + - build-depends on quilt and python-docutils: + + debian/patches/fix_html_docs.patch: fix html documentation for offline + browsing and add another missing doc file; + + rebuild docs at build time in debian/rules; + * debian/example.makefile: install a simple makefile for example programs + included in -doc package. + * debian/rules, debian/python-libtorrent.install: don't rely on hardcoded + python version, use --install-layout=deb when building python bindings, + and install to '*-packages' instead of 'site-packages' (this should + prepare for python2.6 and minimize Ubuntu diff). + + -- Cristian Greco Wed, 13 May 2009 12:08:59 +0200 + +libtorrent-rasterbar (0.14.2-2) unstable; urgency=low + + * debian/control, debian/rules: revert building only against boost1.37 since + the dependencies on the -dev packages are causing some problems with + reverse depends clients and backporting to stable is not really needed + (too much maintenance work) at the moment. + * debian/README.source: deleted. + + -- Cristian Greco Wed, 11 Mar 2009 16:27:20 +0100 + +libtorrent-rasterbar (0.14.2-1) unstable; urgency=low + + * New upstream version. + - new package libtorrent-rasterbar2 (bump up library SONAME); + - remove patch 01_fix_ax_python_m4.patch (merged upstream); + - debian/control, debian/rules, debian/README.source: drop build + dependency on quilt system. + * debian/control, debian/rules, debian/README.source: adjusting build + dependencies and configure flags to build against boost1.37 and continue + to provide compatibility with boost1.34.1 for backports (adding some notes). + * debian/control: fix lintian I:binary-control-field-duplicates-source. + * Moving section from 'Programming' to 'Programming/C++' for the doc-base + files in libtorrent-rasterbar-doc. + * Closing a bug already fixed in 0.14-1: fixed documentation about + storage_mode_allocate (Closes: #502241). + * Upload to unstable (Closes: #517000). + + -- Cristian Greco Fri, 27 Feb 2009 07:46:38 +0100 + +libtorrent-rasterbar (0.14.1-1) experimental; urgency=low + + * New upstream version + - remove patch 01_python_bindings.patch (included upstream); + - add patch 01_fix_ax_python_m4.patch (already merged upstream, see + description for details). + * Revert build-depends on libboost-dev (>= 1.34.1) and libasio-dev because + boost1.36 has been removed from unstable and boost1.37 is not yet fully + supported by the library (added notes on README.Debian). + * Install .cpp examples using dh_installexamples and a .examples file. + * debian/copyright: fix main BSD license text. + + -- Cristian Greco Mon, 15 Dec 2008 15:34:17 +0100 + +libtorrent-rasterbar (0.14-1) experimental; urgency=low + + * New upstream version. + - debian/copyright: new and updated copyright notes and files. + - debian/control: + + build-depends on libboost1.36-dev and python-dev; + + new package libtorrent-rasterbar1 (bump up library SONAME); + + new package python-libtorrent which contains python bindings + for the library. + - debian/rules: + + use multi-threaded boost libraries (--with-boost-system=mt, ...); + + add --disable-examples and --disable-tests options; + + delete specific information about debian revision in dh_makeshlibs + (i.e., use only dh_makeshlibs -V). + - debian/control, debian/rules, debian/README.source, debian/patches: + build-depends on quilt patch system and install a patch of mine + (01_python_bindings, already merged upstream) in order to build python + bindings; use a temporary fix for the 'clean' target. + * Install example files (.cpp) with the -doc package. + * Provide a doc-base control file to install html docs + (libtorrent-rasterbar-doc.doc-base). + * debian/README.Debian: new notes about the python package and the lintian + tag 'no-symbols-control-file' for libtorrent-rasterbar1. + * Close a bug in Ubuntu about build with --disable-debug, already solved + with 0.13.1-2 (LP: #291448). + * Upload to experimental to test the new python bindings package. + * New maintainer email address. + + -- Cristian Greco Sun, 23 Nov 2008 18:55:24 +0100 + +libtorrent-rasterbar (0.13.1-2) unstable; urgency=low + + * debian/rules: + - build with --disable-debug option because enabling debug seems to cause + some clients to crash, and most upstream authors prefer a release build of + the library. + - added specific version information to dh_makeshlibs so that reverse + dependencies will depend on this particular version. + * README.Debian: updated with info about --disable-debug. + + -- Cristian Greco Wed, 23 Jul 2008 11:18:31 +0200 + +libtorrent-rasterbar (0.13.1-1) unstable; urgency=low + + * Initial release (Closes: #402462) + + -- Cristian Greco Wed, 02 Jul 2008 10:46:21 +0200 diff --git a/control b/control new file mode 100644 index 0000000..afe7868 --- /dev/null +++ b/control @@ -0,0 +1,80 @@ +Source: libtorrent-rasterbar +Section: libs +Maintainer: Christian Marillat +Homepage: https://libtorrent.org/ +Standards-Version: 4.7.4 +Build-Depends-Indep: python3-docutils, python3-pygments, python3-aafigure, + graphviz, imagemagick, python3-sphinxcontrib.svg2pdfconverter, icoutils +Build-Depends: dpkg-dev (>= 1.22.5), debhelper-compat (= 14), python3-dev, + dh-sequence-python3, libssl-dev, pkgconf, cmake, ninja-build, + python3-setuptools, libboost-python-dev, + +Package: libtorrent-rasterbar2.0t64 +Provides: ${t64:Provides} +Replaces: libtorrent-rasterbar2.0, +Breaks: libtorrent-rasterbar2.0 (<< ${source:Version}), +Architecture: any +Multi-Arch: same +Depends: ${shlibs:Depends}, ${misc:Depends}, +Description: C++ bittorrent library by Rasterbar Software + Bittorrent library by Rasterbar Software (Arvid Norberg). + libtorrent-rasterbar is a C++ library that aims to be a good alternative to + all the other bittorrent implementations around. + . + The main goals of libtorrent-rasterbar are: + * to be cpu efficient + * to be memory efficient + * to be very easy to use + . + This package contains libtorrent-rasterbar run time shared library. + +Package: libtorrent-rasterbar-dev +Section: libdevel +Architecture: any +Multi-Arch: same +Depends: ${misc:Depends}, libtorrent-rasterbar2.0t64 (= ${binary:Version}), libssl-dev, pkgconf, libboost-dev, +Suggests: libtorrent-rasterbar-doc, +Description: Development files for libtorrent-rasterbar + Bittorrent library by Rasterbar Software (Arvid Norberg). + libtorrent-rasterbar is a C++ library that aims to be a good alternative to + all the other bittorrent implementations around. + . + The main goals of libtorrent-rasterbar are: + * to be cpu efficient + * to be memory efficient + * to be very easy to use + . + This package contains header files and static version of the library. + +Package: libtorrent-rasterbar-doc +Section: doc +Architecture: all +Multi-Arch: foreign +Depends: ${misc:Depends}, +Description: Documentation for libtorrent-rasterbar + Bittorrent library by Rasterbar Software (Arvid Norberg). + libtorrent-rasterbar is a C++ library that aims to be a good alternative to + all the other bittorrent implementations around. + . + The main goals of libtorrent-rasterbar are: + * to be cpu efficient + * to be memory efficient + * to be very easy to use + . + This package contains documentation and example files. + +Package: python3-libtorrent +Section: python +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, ${python3:Depends}, +Description: Python bindings for libtorrent-rasterbar (Python 3) + Bittorrent library by Rasterbar Software (Arvid Norberg). + libtorrent-rasterbar is a C++ library that aims to be a good alternative to + all the other bittorrent implementations around. + . + The main goals of libtorrent-rasterbar are: + * to be cpu efficient + * to be memory efficient + * to be very easy to use + . + This package contains Python 3 bindings for the libtorrent-rasterbar library. diff --git a/copyright b/copyright new file mode 100644 index 0000000..aaa19cc --- /dev/null +++ b/copyright @@ -0,0 +1,147 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Packaged-By: Cristian Greco +Upstream-Name: libtorrent +Upstream-Contact: Arvid Norberg +Source: https://github.com/arvidn/libtorrent/releases +Files-Excluded: + .* + +Files: * +Copyright: 2003-2026, Arvid Norberg + 2014-2026, Alden Torres + 2005-2026, Steven Siloti + 2003-2026, Daniel Wallin + 2006-2026, Magnus Jonsson + 2007-2026, Un Shyam + 2006-2026, MassaRoddel + 2006-2026, Thomas Yuan +License: BSD-3-clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + . + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +Files: debian/* +Copyright: 2021-2026, Christian Marillat + 2008, Cristian Greco +License: GPL-2+ + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + . + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. +Comment: + On Debian systems, the full text of the GNU General Public License version 2 + can be found in the file "/usr/share/common-licenses/GPL-2". + +Files: bindings/python/* +Copyright: 2004-2007, Daniel Wallin + 2006-2008, Arvid Norberg + 2008, Andrew Resch + 2015, John Maddock +License: BSL-1.0 + Boost Software License - Version 1.0 - August 17th, 2003 + . + Permission is hereby granted, free of charge, to any person or organization + obtaining a copy of the software and accompanying documentation covered by + this license (the "Software") to use, reproduce, display, distribute, + execute, and transmit the Software, and to prepare derivative works of the + Software, and to permit third-parties to whom the Software is furnished to + do so, all subject to the following: + . + The copyright notices in the Software and this entire statement, including + the above license grant, this restriction and the following disclaimer, + must be included in all copies of the Software, in whole or in part, and + all derivative works of the Software, unless such copies or derivative + works are solely in the form of machine-executable object code generated by + a source language processor. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + +Files: include/libtorrent/puff.hpp + src/puff.cpp +Copyright: 2002-2003, Mark Adler +License: Zlib + This software is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + . + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + . + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + 3. This notice may not be removed or altered from any source + distribution. + +Files: src/sha1.cpp + include/libtorrent/sha1.hpp +Copyright: Steve Reid +Comment: Contributors: + Modified 7/98 By James H. Brown + Modified 8/98 By Steve Reid + Modified 4/01 By Saul Kravitz + Converted to C++ 6/04 By Arvid Norberg +License: public-domain + The file sha1.c was put in the public domain by Steve Reid. + +Files: cmake/Modules/ucm_flags.cmake +Copyright: 2016, Viktor Kirilov + 1994, X Consortium +License: Expat + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + . + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. diff --git a/docs b/docs new file mode 100644 index 0000000..62deb04 --- /dev/null +++ b/docs @@ -0,0 +1 @@ +AUTHORS diff --git a/examples.makefile b/examples.makefile new file mode 100644 index 0000000..3722d53 --- /dev/null +++ b/examples.makefile @@ -0,0 +1,50 @@ +#!/usr/bin/make -f +# +# Simple makefile for libtorrent-rasterbar's examples. +# Copyright © 2009 Cristian Greco +# This file is released in the public domain. +# +# Please note that you need to install the following packages in order to build +# these example programs: +# - libtorrent-rasterbar-dev +# - libboost-program-options*-dev +# - libboost-regex*-dev +# (where the `*' means the same version of boost development packages which +# libtorrent-rasterbar-dev actually depends on). + +CXX = g++ + +CXXFLAGS = -ftemplate-depth-50 -DBOOST_MULTI_INDEX_DISABLE_SERIALIZATION + +TORRENT_CFLAGS = $(shell pkg-config libtorrent-rasterbar --cflags) +TORRENT_LIBS = $(shell pkg-config libtorrent-rasterbar --libs) + +BOOST_PROGRAM_OPTIONS_LIBS = -lboost_program_options-mt +BOOST_REGEX_LIBS = -lboost_regex-mt + +examples_BIN = client_test dump_torrent make_torrent simple_client enum_if + +all: $(examples_BIN) + +client_test: client_test.cpp + @rm -f client_test + $(CXX) $(CXXFLAGS) $(TORRENT_CFLAGS) -o $@ $< $(TORRENT_LIBS) $(BOOST_PROGRAM_OPTIONS_LIBS) $(BOOST_REGEX_LIBS) + +dump_torrent: dump_torrent.cpp + @rm -f dump_torrent + $(CXX) $(CXXFLAGS) $(TORRENT_CFLAGS) -o $@ $< $(TORRENT_LIBS) + +make_torrent: make_torrent.cpp + @rm -f make_torrent + $(CXX) $(CXXFLAGS) $(TORRENT_CFLAGS) -o $@ $< $(TORRENT_LIBS) + +simple_client: simple_client.cpp + @rm -f simple_client + $(CXX) $(CXXFLAGS) $(TORRENT_CFLAGS) -o $@ $< $(TORRENT_LIBS) + +enum_if: enum_if.cpp + @rm -f enum_if + $(CXX) $(CXXFLAGS) $(TORRENT_CFLAGS) -o $@ $< $(TORRENT_LIBS) + +clean: + @rm -f $(examples_BIN) diff --git a/libtorrent-rasterbar-dev.install b/libtorrent-rasterbar-dev.install new file mode 100644 index 0000000..7b90b0c --- /dev/null +++ b/libtorrent-rasterbar-dev.install @@ -0,0 +1,5 @@ +usr/include +usr/lib/*/lib*.so +usr/lib/*/pkgconfig +usr/lib/*/cmake +usr/share/cmake diff --git a/libtorrent-rasterbar-doc.doc-base b/libtorrent-rasterbar-doc.doc-base new file mode 100644 index 0000000..6c8e9f1 --- /dev/null +++ b/libtorrent-rasterbar-doc.doc-base @@ -0,0 +1,11 @@ +Document: libtorrent-rasterbar +Section: Programming/C++ +Title: Libtorrent-rasterbar Documentation +Author: Arvid Norberg +Abstract: This documentation describes mainly the API + interface of libtorrent-rasterbar and contains other + useful information for developers. + +Format: HTML +Index: /usr/share/doc/libtorrent-rasterbar-doc/html/index.html +Files: /usr/share/doc/libtorrent-rasterbar-doc/html/*.html diff --git a/libtorrent-rasterbar-doc.examples b/libtorrent-rasterbar-doc.examples new file mode 100644 index 0000000..8422eb5 --- /dev/null +++ b/libtorrent-rasterbar-doc.examples @@ -0,0 +1,2 @@ +examples/*.cpp +debian/examples.makefile diff --git a/libtorrent-rasterbar-doc.install b/libtorrent-rasterbar-doc.install new file mode 100644 index 0000000..b425fc2 --- /dev/null +++ b/libtorrent-rasterbar-doc.install @@ -0,0 +1,3 @@ +docs/*.html usr/share/doc/libtorrent-rasterbar-doc/html/ +docs/style.css usr/share/doc/libtorrent-rasterbar-doc/html/ +docs/img/* usr/share/doc/libtorrent-rasterbar-doc/html/img/ diff --git a/libtorrent-rasterbar2.0.install b/libtorrent-rasterbar2.0.install new file mode 100644 index 0000000..3ddde58 --- /dev/null +++ b/libtorrent-rasterbar2.0.install @@ -0,0 +1 @@ +usr/lib/*/lib*.so.* diff --git a/libtorrent-rasterbar2.0t64.install b/libtorrent-rasterbar2.0t64.install new file mode 100644 index 0000000..3ddde58 --- /dev/null +++ b/libtorrent-rasterbar2.0t64.install @@ -0,0 +1 @@ +usr/lib/*/lib*.so.* diff --git a/libtorrent-rasterbar2.0t64.lintian-overrides b/libtorrent-rasterbar2.0t64.lintian-overrides new file mode 100644 index 0000000..43558c6 --- /dev/null +++ b/libtorrent-rasterbar2.0t64.lintian-overrides @@ -0,0 +1 @@ +libtorrent-rasterbar2.0t64: package-name-doesnt-match-sonames libtorrent-rasterbar2.0 diff --git a/patches/01_git-submodules.patch b/patches/01_git-submodules.patch new file mode 100644 index 0000000..3fd49cb --- /dev/null +++ b/patches/01_git-submodules.patch @@ -0,0 +1,37734 @@ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/deps/try_signal/.travis.yml +@@ -0,0 +1,44 @@ ++language: cpp ++matrix: ++ include: ++ - env: toolset=gcc ++ - os: osx ++ osx_image: xcode11.2 ++ env: toolset=darwin ++ ++branches: ++ only: ++ - master ++ ++git: ++ submodules: false ++ depth: 1 ++ ++addons: ++ apt: ++ sources: ++ - ubuntu-toolchain-r-test ++ packages: ++ - libboost-tools-dev ++ - g++-9 ++ ++install: ++ ++ - 'if [[ $TRAVIS_OS_NAME == "osx" ]]; then brew update > /dev/null && brew install boost-build; fi' ++ - 'if [[ $TRAVIS_OS_NAME != "osx" ]]; then ++ export B2=bjam; ++ else ++ export B2=b2; ++ fi' ++ - touch ~/user-config.jam ++ - 'if [[ $toolset == "gcc" ]]; then ++ g++-5 --version; ++ echo "using gcc : : g++-5 : -std=c++11 ;" >> ~/user-config.jam; ++ fi' ++ - 'echo "using darwin : : clang++ : -std=c++11 ;" >> ~/user-config.jam' ++ ++script: ++ ++ - ${B2} link=static stage_test ++ - ./test ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/deps/try_signal/CMakeLists.txt +@@ -0,0 +1,6 @@ ++cmake_minimum_required(VERSION 2.8.12) ++project(try_signal) ++ ++add_library(try_signal signal_error_code try_signal) ++target_include_directories(try_signal PUBLIC .) ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/deps/try_signal/Jamfile +@@ -0,0 +1,18 @@ ++lib try_signal ++ : # sources ++ signal_error_code.cpp try_signal.cpp ++ : # requirements ++ : # default build ++ static ++ : # usage requirements ++ . ++ ; ++ ++exe test : test.cpp : try_signal static ; ++explicit test ; ++ ++exe example : example.cpp : try_signal static ; ++explicit example ; ++ ++install stage_test : test : . ; ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/deps/try_signal/LICENSE +@@ -0,0 +1,29 @@ ++BSD 3-Clause License ++ ++Copyright (c) 2016, Arvid Norberg ++All rights reserved. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions are met: ++ ++* Redistributions of source code must retain the above copyright notice, this ++ list of conditions and the following disclaimer. ++ ++* Redistributions in binary form must reproduce the above copyright notice, ++ this list of conditions and the following disclaimer in the documentation ++ and/or other materials provided with the distribution. ++ ++* Neither the name of the copyright holder nor the names of its ++ contributors may be used to endorse or promote products derived from ++ this software without specific prior written permission. ++ ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ++DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE ++FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ++OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/deps/try_signal/README.rst +@@ -0,0 +1,53 @@ ++try_signal ++========== ++ ++.. image:: https://travis-ci.org/arvidn/try_signal.svg?branch=master ++ :target: https://travis-ci.org/arvidn/try_signal ++ ++.. image:: https://ci.appveyor.com/api/projects/status/le8jjroaai8081f1?svg=true ++ :target: https://ci.appveyor.com/project/arvidn/try-signal/branch/master ++ ++The ``try_signal`` library provide a way to turn signals into C++ exceptions. ++This is especially useful when performing disk I/O via memory mapped files, ++where I/O errors are reported as ``SIGBUS`` and ``SIGSEGV`` or as structured ++exceptions on windows. ++ ++The function ``try_signal`` takes a function object that will be executed once. ++If the function causes a signal (or structured exception) to be raised, it will ++throw a C++ exception. Note that RAII may not be relied upon within this function. ++It may not rely on destructors being called. Stick to simple operations like ++memcopy. ++ ++Example:: ++ ++ #include ++ #include ++ #include ++ #include "try_signal.hpp" ++ #include ++ #include ++ #include ++ ++ int main() try ++ { ++ int fd = open("test_file", O_RDWR); ++ void* map = mmap(nullptr, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); ++ ++ std::vector buf(1024); ++ std::iota(buf.begin(), buf.end(), 0); ++ ++ // disk full or access after EOF are reported as exceptions ++ sig::try_signal([&]{ ++ std::memcpy(map, buf.data(), buf.size()); ++ }); ++ ++ munmap(map, 1024); ++ close(fd); ++ return 0; ++ } ++ catch (std::exception const& e) ++ { ++ fprintf(stderr, "exited with exception: %s\n", e.what()); ++ return 1; ++ } ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/deps/try_signal/appveyor.yml +@@ -0,0 +1,50 @@ ++version: "{build}" ++branches: ++ only: ++ - master ++os: Visual Studio 2015 ++clone_depth: 1 ++environment: ++ matrix: ++ - variant: debug ++ compiler: msvc-14.0 ++ model: 64 ++ - variant: debug ++ compiler: msvc-14.0 ++ model: 32 ++ - variant: release ++ compiler: msvc-14.0 ++ model: 64 ++ - variant: debug ++ compiler: gcc ++ model: 32 ++ - variant: debug ++ compiler: gcc ++ model: 64 ++ - variant: release ++ compiler: gcc ++ model: 32 ++ ++install: ++- set ROOT_DIRECTORY=%CD% ++- set BOOST_ROOT=c:\Libraries\boost_1_67_0 ++- set BOOST_BUILD_PATH=%BOOST_ROOT%\tools\build ++- echo %BOOST_ROOT% ++- echo %BOOST_BUILD_PATH% ++- set PATH=%PATH%;%BOOST_BUILD_PATH%\src\engine\bin.ntx86 ++- ps: '"using msvc : 14.0 ;`nusing gcc : : : -std=c++11 ;" | Set-Content $env:HOMEDRIVE\$env:HOMEPATH\user-config.jam' ++- type %HOMEDRIVE%%HOMEPATH%\user-config.jam ++- set PATH=c:\msys64\mingw32\bin;%PATH% ++- g++ --version ++- python --version ++- echo %ROOT_DIRECTORY% ++- cd %BOOST_BUILD_PATH%\src\engine ++- build.bat >nul ++- cd %ROOT_DIRECTORY% ++ ++build_script: ++# examples ++- b2.exe warnings=all warnings-as-errors=on -j2 %compiler% address-model=%model% variant=%variant% stage_test ++ ++test_script: ++- test +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/deps/try_signal/example.cpp +@@ -0,0 +1,31 @@ ++#include ++#include ++#include ++#include "try_signal.hpp" ++#include ++#include ++#include ++ ++int main() try ++{ ++ int fd = open("test_file", O_RDWR); ++ void* map = mmap(nullptr, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); ++ ++ std::vector buf(1024); ++ std::iota(buf.begin(), buf.end(), 0); ++ ++ // disk full or access after EOF are reported as exceptions ++ sig::try_signal([&]{ ++ std::memcpy(map, buf.data(), buf.size()); ++ }); ++ ++ munmap(map, 1024); ++ close(fd); ++ return 0; ++} ++catch (std::exception const& e) ++{ ++ fprintf(stderr, "exited with exception: %s\n", e.what()); ++ return 1; ++} ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/deps/try_signal/signal_error_code.cpp +@@ -0,0 +1,209 @@ ++/* ++ ++Copyright (c) 2016, Arvid Norberg ++All rights reserved. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions ++are met: ++ ++ * Redistributions of source code must retain the above copyright ++ notice, this list of conditions and the following disclaimer. ++ * Redistributions in binary form must reproduce the above copyright ++ notice, this list of conditions and the following disclaimer in ++ the documentation and/or other materials provided with the distribution. ++ * Neither the name of the author nor the names of its ++ contributors may be used to endorse or promote products derived ++ from this software without specific prior written permission. ++ ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++POSSIBILITY OF SUCH DAMAGE. ++ ++*/ ++ ++#include ++#include ++ ++#include "signal_error_code.hpp" ++ ++namespace { ++ ++ struct signal_error_category : std::error_category ++ { ++ const char* name() const noexcept override ++ { return "signal"; } ++ std::string message(int ev) const noexcept override ++ { ++#define SIGNAL_CASE(x) case sig::errors::error_code_enum:: x: return #x; ++ switch (ev) ++ { ++ SIGNAL_CASE(abort) ++ SIGNAL_CASE(alarm) ++ SIGNAL_CASE(arithmetic_exception) ++ SIGNAL_CASE(hangup) ++ SIGNAL_CASE(illegal) ++ SIGNAL_CASE(interrupt) ++ SIGNAL_CASE(kill) ++ SIGNAL_CASE(pipe) ++ SIGNAL_CASE(quit) ++ case sig::errors::error_code_enum::segmentation: return "segmentation fault"; ++ SIGNAL_CASE(terminate) ++ SIGNAL_CASE(user1) ++ SIGNAL_CASE(user2) ++ SIGNAL_CASE(child) ++ SIGNAL_CASE(cont) ++ SIGNAL_CASE(stop) ++ SIGNAL_CASE(terminal_stop) ++ SIGNAL_CASE(terminal_in) ++ SIGNAL_CASE(terminal_out) ++ SIGNAL_CASE(bus) ++#ifdef SIGPOLL ++ SIGNAL_CASE(poll) ++#endif ++ SIGNAL_CASE(profiler) ++ SIGNAL_CASE(system_call) ++ SIGNAL_CASE(trap) ++ SIGNAL_CASE(urgent_data) ++ SIGNAL_CASE(virtual_timer) ++ SIGNAL_CASE(cpu_limit) ++ SIGNAL_CASE(file_size_limit) ++ default: return "unknown"; ++ } ++#undef SIGNAL_CASE ++ } ++ std::error_condition default_error_condition(int ev) const noexcept override ++ { return {ev, *this}; } ++ }; ++} // anonymous namespace ++ ++namespace sig { ++namespace errors { ++ ++ std::error_code make_error_code(error_code_enum e) ++ { ++ return {e, sig_category()}; ++ } ++ ++ std::error_condition make_error_condition(error_code_enum e) ++ { ++ return {e, sig_category()}; ++ } ++ ++} // namespace errors ++ ++std::error_category& sig_category() ++{ ++ static signal_error_category signal_category; ++ return signal_category; ++} ++ ++#ifdef _WIN32 ++ ++namespace { ++ sig::errors::error_code_enum map_exception_code(int const ev) ++ { ++ switch (ev) ++ { ++ case seh_errors::error_code_enum::access_violation: ++ case seh_errors::error_code_enum::array_bounds_exceeded: ++ case seh_errors::error_code_enum::guard_page: ++ case seh_errors::error_code_enum::stack_overflow: ++ case seh_errors::error_code_enum::flt_stack_check: ++ case seh_errors::error_code_enum::in_page_error: ++ return sig::errors::segmentation; ++ case seh_errors::error_code_enum::breakpoint: ++ case seh_errors::error_code_enum::single_step: ++ return sig::errors::trap; ++ case seh_errors::error_code_enum::datatype_misalignment: ++ return sig::errors::bus; ++ case seh_errors::error_code_enum::flt_denormal_operand: ++ case seh_errors::error_code_enum::flt_divide_by_zero: ++ case seh_errors::error_code_enum::flt_inexact_result: ++ case seh_errors::error_code_enum::flt_invalid_operation: ++ case seh_errors::error_code_enum::flt_overflow: ++ case seh_errors::error_code_enum::flt_underflow: ++ case seh_errors::error_code_enum::int_divide_by_zero: ++ case seh_errors::error_code_enum::int_overflow: ++ return sig::errors::arithmetic_exception; ++ case seh_errors::error_code_enum::illegal_instruction: ++ case seh_errors::error_code_enum::invalid_disposition: ++ case seh_errors::error_code_enum::priv_instruction: ++ case seh_errors::error_code_enum::noncontinuable_exception: ++ case seh_errors::error_code_enum::status_unwind_consolidate: ++ return sig::errors::illegal; ++ case seh_errors::error_code_enum::invalid_handle: ++ return sig::errors::pipe; ++ default: ++ return sig::errors::illegal; ++ } ++ } ++ ++ struct seh_error_category : std::error_category ++ { ++ const char* name() const noexcept override ++ { return "SEH"; } ++ std::string message(int ev) const noexcept override ++ { ++#define SIGNAL_CASE(x) case sig::seh_errors::error_code_enum:: x: return #x; ++ switch (ev) ++ { ++ SIGNAL_CASE(access_violation) ++ SIGNAL_CASE(array_bounds_exceeded) ++ SIGNAL_CASE(guard_page) ++ SIGNAL_CASE(stack_overflow) ++ SIGNAL_CASE(flt_stack_check) ++ SIGNAL_CASE(in_page_error) ++ SIGNAL_CASE(breakpoint) ++ SIGNAL_CASE(single_step) ++ SIGNAL_CASE(datatype_misalignment) ++ SIGNAL_CASE(flt_denormal_operand) ++ SIGNAL_CASE(flt_divide_by_zero) ++ SIGNAL_CASE(flt_inexact_result) ++ SIGNAL_CASE(flt_invalid_operation) ++ SIGNAL_CASE(flt_overflow) ++ SIGNAL_CASE(flt_underflow) ++ SIGNAL_CASE(int_divide_by_zero) ++ SIGNAL_CASE(int_overflow) ++ SIGNAL_CASE(illegal_instruction) ++ SIGNAL_CASE(invalid_disposition) ++ SIGNAL_CASE(priv_instruction) ++ SIGNAL_CASE(noncontinuable_exception) ++ SIGNAL_CASE(status_unwind_consolidate) ++ SIGNAL_CASE(invalid_handle) ++ default: return "unknown"; ++ } ++#undef SIGNAL_CASE ++ } ++ std::error_condition default_error_condition(int ev) const noexcept override ++ { return std::error_condition(map_exception_code(ev), sig_category()); } ++ }; ++} // anonymous namespace ++ ++namespace seh_errors { ++ ++ std::error_code make_error_code(error_code_enum e) ++ { ++ return {static_cast(e), seh_category()}; ++ } ++ ++} // namespace errors ++ ++std::error_category& seh_category() ++{ ++ static seh_error_category seh_category; ++ return seh_category; ++} ++ ++#endif ++ ++} // namespace sig ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/deps/try_signal/signal_error_code.hpp +@@ -0,0 +1,162 @@ ++/* ++ ++Copyright (c) 2016, Arvid Norberg ++All rights reserved. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions ++are met: ++ ++ * Redistributions of source code must retain the above copyright ++ notice, this list of conditions and the following disclaimer. ++ * Redistributions in binary form must reproduce the above copyright ++ notice, this list of conditions and the following disclaimer in ++ the documentation and/or other materials provided with the distribution. ++ * Neither the name of the author nor the names of its ++ contributors may be used to endorse or promote products derived ++ from this software without specific prior written permission. ++ ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++POSSIBILITY OF SUCH DAMAGE. ++ ++*/ ++ ++#ifndef SIGNAL_ERROR_CODE_HPP_INCLUDED ++#define SIGNAL_ERROR_CODE_HPP_INCLUDED ++ ++#include ++#include ++ ++#ifdef _WIN32 ++#ifndef WIN32_LEAN_AND_MEAN ++#define WIN32_LEAN_AND_MEAN ++#endif ++#include ++ ++#ifdef __GNUC__ ++#include ++#else ++#include ++#endif ++#endif ++ ++namespace sig { ++namespace errors { ++ ++#ifdef _WIN32 ++#define SIG_ENUM(name, sig) name, ++#else ++#define SIG_ENUM(name, sig) name = sig, ++#endif ++ ++ enum error_code_enum: int ++ { ++ SIG_ENUM(abort, SIGABRT) ++ SIG_ENUM(alarm, SIGALRM) ++ SIG_ENUM(arithmetic_exception, SIGFPE) ++ SIG_ENUM(hangup, SIGHUP) ++ SIG_ENUM(illegal, SIGILL) ++ SIG_ENUM(interrupt, SIGINT) ++ SIG_ENUM(kill, SIGKILL) ++ SIG_ENUM(pipe, SIGPIPE) ++ SIG_ENUM(quit, SIGQUIT) ++ SIG_ENUM(segmentation, SIGSEGV) ++ SIG_ENUM(terminate, SIGTERM) ++ SIG_ENUM(user1, SIGUSR1) ++ SIG_ENUM(user2, SIGUSR2) ++ SIG_ENUM(child, SIGCHLD) ++ SIG_ENUM(cont, SIGCONT) ++ SIG_ENUM(stop, SIGSTOP) ++ SIG_ENUM(terminal_stop, SIGTSTP) ++ SIG_ENUM(terminal_in, SIGTTIN) ++ SIG_ENUM(terminal_out, SIGTTOU) ++ SIG_ENUM(bus, SIGBUS) ++#ifdef SIGPOLL ++ SIG_ENUM(poll, SIGPOLL) ++#endif ++ SIG_ENUM(profiler, SIGPROF) ++ SIG_ENUM(system_call, SIGSYS) ++ SIG_ENUM(trap, SIGTRAP) ++ SIG_ENUM(urgent_data, SIGURG) ++ SIG_ENUM(virtual_timer, SIGVTALRM) ++ SIG_ENUM(cpu_limit, SIGXCPU) ++ SIG_ENUM(file_size_limit, SIGXFSZ) ++ }; ++ ++#undef SIG_ENUM ++ ++ std::error_code make_error_code(error_code_enum e); ++ std::error_condition make_error_condition(error_code_enum e); ++ ++} // namespace errors ++ ++std::error_category& sig_category(); ++ ++#ifdef _WIN32 ++namespace seh_errors { ++ ++ // standard error codes are "int", the win32 exceptions are DWORD (i.e. ++ // unsigned int). We coerce them into int here for compatibility, and we're ++ // not concerned about their arithmetic ++ enum error_code_enum: int ++ { ++ access_violation = int(EXCEPTION_ACCESS_VIOLATION), ++ array_bounds_exceeded = int(EXCEPTION_ARRAY_BOUNDS_EXCEEDED), ++ guard_page = int(EXCEPTION_GUARD_PAGE), ++ stack_overflow = int(EXCEPTION_STACK_OVERFLOW), ++ flt_stack_check = int(EXCEPTION_FLT_STACK_CHECK), ++ in_page_error = int(EXCEPTION_IN_PAGE_ERROR), ++ breakpoint = int(EXCEPTION_BREAKPOINT), ++ single_step = int(EXCEPTION_SINGLE_STEP), ++ datatype_misalignment = int(EXCEPTION_DATATYPE_MISALIGNMENT), ++ flt_denormal_operand = int(EXCEPTION_FLT_DENORMAL_OPERAND), ++ flt_divide_by_zero = int(EXCEPTION_FLT_DIVIDE_BY_ZERO), ++ flt_inexact_result = int(EXCEPTION_FLT_INEXACT_RESULT), ++ flt_invalid_operation = int(EXCEPTION_FLT_INVALID_OPERATION), ++ flt_overflow = int(EXCEPTION_FLT_OVERFLOW), ++ flt_underflow = int(EXCEPTION_FLT_UNDERFLOW), ++ int_divide_by_zero = int(EXCEPTION_INT_DIVIDE_BY_ZERO), ++ int_overflow = int(EXCEPTION_INT_OVERFLOW), ++ illegal_instruction = int(EXCEPTION_ILLEGAL_INSTRUCTION), ++ invalid_disposition = int(EXCEPTION_INVALID_DISPOSITION), ++ priv_instruction = int(EXCEPTION_PRIV_INSTRUCTION), ++ noncontinuable_exception = int(EXCEPTION_NONCONTINUABLE_EXCEPTION), ++ status_unwind_consolidate = int(STATUS_UNWIND_CONSOLIDATE), ++ invalid_handle = int(EXCEPTION_INVALID_HANDLE), ++ }; ++ ++ std::error_code make_error_code(error_code_enum e); ++} ++ ++std::error_category& seh_category(); ++ ++#endif // _WIN32 ++ ++} // namespace sig ++ ++namespace std ++{ ++template<> ++struct is_error_code_enum : std::true_type {}; ++ ++template<> ++struct is_error_condition_enum : std::true_type {}; ++ ++#ifdef _WIN32 ++template<> ++struct is_error_code_enum : std::true_type {}; ++#endif ++ ++} // namespace std ++ ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/deps/try_signal/test.cpp +@@ -0,0 +1,47 @@ ++#include ++#include ++#include // for memcpy ++ ++#include "try_signal.hpp" ++ ++int main() ++{ ++ char const buf[] = "test...test"; ++ char dest[sizeof(buf)]; ++ ++ { ++ sig::try_signal([&]{ ++ std::memcpy(dest, buf, sizeof(buf)); ++ }); ++ if (!std::equal(buf, buf + sizeof(buf), dest)) { ++ fprintf(stderr, "ERROR: buffer not copied correctly\n"); ++ return 1; ++ } ++ } ++ ++ try { ++ void* invalid_pointer = nullptr; ++ sig::try_signal([&]{ ++ std::memcpy(dest, buf, sizeof(buf)); ++ std::memcpy(dest, invalid_pointer, sizeof(buf)); ++ }); ++ } ++ catch (std::system_error const& e) ++ { ++ if (e.code() != std::error_condition(sig::errors::segmentation)) { ++ fprintf(stderr, "ERROR: expected segmentaiton violation error\n"); ++ } ++ else { ++ fprintf(stderr, "OK\n"); ++ } ++ fprintf(stderr, "exited with expected system_error exception: %s\n", e.what()); ++ ++ // we expect this to happen, so return 0 ++ return e.code() == std::error_condition(sig::errors::segmentation) ? 0 : 1; ++ } ++ ++ // return non-zero here because we don't expect this ++ fprintf(stderr, "ERROR: expected exit through exception\n"); ++ return 1; ++} ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/deps/try_signal/try_signal.cpp +@@ -0,0 +1,144 @@ ++/* ++ ++Copyright (c) 2016, Arvid Norberg ++All rights reserved. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions ++are met: ++ ++ * Redistributions of source code must retain the above copyright ++ notice, this list of conditions and the following disclaimer. ++ * Redistributions in binary form must reproduce the above copyright ++ notice, this list of conditions and the following disclaimer in ++ the documentation and/or other materials provided with the distribution. ++ * Neither the name of the author nor the names of its ++ contributors may be used to endorse or promote products derived ++ from this software without specific prior written permission. ++ ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++POSSIBILITY OF SUCH DAMAGE. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "try_signal.hpp" ++ ++#if !defined _WIN32 ++// linux ++ ++namespace sig { ++namespace detail { ++ ++namespace { ++thread_local sigjmp_buf* jmpbuf = nullptr; ++} ++ ++std::atomic_flag once = ATOMIC_FLAG_INIT; ++ ++scoped_jmpbuf::scoped_jmpbuf(sigjmp_buf* ptr) ++{ ++ _previous_ptr = jmpbuf; ++ jmpbuf = ptr; ++ std::atomic_signal_fence(std::memory_order_release); ++} ++ ++scoped_jmpbuf::~scoped_jmpbuf() { jmpbuf = _previous_ptr; } ++ ++void handler(int const signo, siginfo_t*, void*) ++{ ++ std::atomic_signal_fence(std::memory_order_acquire); ++ if (jmpbuf) ++ siglongjmp(*jmpbuf, signo); ++ ++ // this signal was not caused within the scope of a try_signal object, ++ // invoke the default handler ++ signal(signo, SIG_DFL); ++ raise(signo); ++} ++ ++void setup_handler() ++{ ++ struct sigaction sa; ++ sa.sa_sigaction = &sig::detail::handler; ++ sigemptyset(&sa.sa_mask); ++ sa.sa_flags = SA_SIGINFO; ++ sigaction(SIGSEGV, &sa, nullptr); ++ sigaction(SIGBUS, &sa, nullptr); ++} ++ ++} // detail namespace ++} // sig namespace ++ ++#elif __GNUC__ ++// mingw ++ ++#ifndef WIN32_LEAN_AND_MEAN ++#define WIN32_LEAN_AND_MEAN ++#endif ++#include ++ ++namespace sig { ++namespace detail { ++ ++thread_local jmp_buf* jmpbuf = nullptr; ++ ++long CALLBACK handler(EXCEPTION_POINTERS* pointers) ++{ ++ std::atomic_signal_fence(std::memory_order_acquire); ++ if (jmpbuf) ++ longjmp(*jmpbuf, pointers->ExceptionRecord->ExceptionCode); ++ return EXCEPTION_CONTINUE_SEARCH; ++} ++ ++scoped_handler::scoped_handler(jmp_buf* ptr) ++{ ++ _previous_ptr = jmpbuf; ++ jmpbuf = ptr; ++ std::atomic_signal_fence(std::memory_order_release); ++ _handle = AddVectoredExceptionHandler(1, sig::detail::handler); ++} ++scoped_handler::~scoped_handler() ++{ ++ RemoveVectoredExceptionHandler(_handle); ++ jmpbuf = _previous_ptr; ++} ++ ++} // detail namespace ++} // sig namespace ++ ++#else ++// windows ++ ++#include // for EXCEPTION_* ++ ++namespace sig { ++namespace detail { ++ ++ // these are the kinds of SEH exceptions we'll translate into C++ exceptions ++ bool catch_error(int const code) ++ { ++ return code == EXCEPTION_IN_PAGE_ERROR ++ || code == EXCEPTION_ACCESS_VIOLATION ++ || code == EXCEPTION_ARRAY_BOUNDS_EXCEEDED; ++ } ++} // detail namespace ++} // namespace sig ++ ++#endif // _WIN32 ++ ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/deps/try_signal/try_signal.hpp +@@ -0,0 +1,49 @@ ++/* ++ ++Copyright (c) 2017, Arvid Norberg ++All rights reserved. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions ++are met: ++ ++ * Redistributions of source code must retain the above copyright ++ notice, this list of conditions and the following disclaimer. ++ * Redistributions in binary form must reproduce the above copyright ++ notice, this list of conditions and the following disclaimer in ++ the documentation and/or other materials provided with the distribution. ++ * Neither the name of the author nor the names of its ++ contributors may be used to endorse or promote products derived ++ from this software without specific prior written permission. ++ ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++POSSIBILITY OF SUCH DAMAGE. ++ ++*/ ++ ++#ifndef TRY_SIGNAL_HPP_INCLUDED ++#define TRY_SIGNAL_HPP_INCLUDED ++ ++#if !defined _WIN32 ++// linux ++#include "try_signal_posix.hpp" ++#elif __GNUC__ ++// mingw ++#include "try_signal_mingw.hpp" ++#else ++// windows ++#include "try_signal_msvc.hpp" ++#endif ++ ++ ++#endif // TRY_SIGNAL_HPP_INCLUDED ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/deps/try_signal/try_signal_mingw.hpp +@@ -0,0 +1,78 @@ ++/* ++ ++Copyright (c) 2017, Arvid Norberg ++All rights reserved. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions ++are met: ++ ++ * Redistributions of source code must retain the above copyright ++ notice, this list of conditions and the following disclaimer. ++ * Redistributions in binary form must reproduce the above copyright ++ notice, this list of conditions and the following disclaimer in ++ the documentation and/or other materials provided with the distribution. ++ * Neither the name of the author nor the names of its ++ contributors may be used to endorse or promote products derived ++ from this software without specific prior written permission. ++ ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++POSSIBILITY OF SUCH DAMAGE. ++ ++*/ ++ ++#ifndef TRY_SIGNAL_MINGW_HPP_INCLUDED ++#define TRY_SIGNAL_MINGW_HPP_INCLUDED ++ ++#include "signal_error_code.hpp" ++ ++#include // for jmp_buf ++ ++#ifndef WIN32_LEAN_AND_MEAN ++#define WIN32_LEAN_AND_MEAN ++#endif ++#include ++ ++namespace sig { ++namespace detail { ++ ++struct scoped_handler ++{ ++ scoped_handler(jmp_buf* ptr); ++ ~scoped_handler(); ++ scoped_handler(scoped_handler const&) = delete; ++ scoped_handler& operator=(scoped_handler const&) = delete; ++private: ++ void* _handle; ++ jmp_buf* _previous_ptr; ++}; ++ ++} // detail namespace ++ ++template ++void try_signal(Fun&& f) ++{ ++ jmp_buf buf; ++ int const code = setjmp(buf); ++ // set the thread local jmpbuf pointer, and make sure it's cleared when we ++ // leave the scope ++ sig::detail::scoped_handler scope(&buf); ++ if (code != 0) ++ throw std::system_error(std::error_code(code, seh_category())); ++ ++ f(); ++} ++ ++} // sig namespace ++ ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/deps/try_signal/try_signal_msvc.hpp +@@ -0,0 +1,61 @@ ++/* ++ ++Copyright (c) 2017, Arvid Norberg ++All rights reserved. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions ++are met: ++ ++ * Redistributions of source code must retain the above copyright ++ notice, this list of conditions and the following disclaimer. ++ * Redistributions in binary form must reproduce the above copyright ++ notice, this list of conditions and the following disclaimer in ++ the documentation and/or other materials provided with the distribution. ++ * Neither the name of the author nor the names of its ++ contributors may be used to endorse or promote products derived ++ from this software without specific prior written permission. ++ ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++POSSIBILITY OF SUCH DAMAGE. ++ ++*/ ++ ++#ifndef TRY_SIGNAL_MSVC_HPP_INCLUDED ++#define TRY_SIGNAL_MSVC_HPP_INCLUDED ++ ++#include "signal_error_code.hpp" ++ ++namespace sig { ++namespace detail { ++ ++bool catch_error(int const code); ++ ++} // detail namespace ++ ++template ++void try_signal(Fun&& f) ++{ ++ __try ++ { ++ f(); ++ } ++ __except (detail::catch_error(GetExceptionCode())) ++ { ++ throw std::system_error(std::error_code(GetExceptionCode(), seh_category())); ++ } ++} ++ ++} // sig namespace ++ ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/deps/try_signal/try_signal_posix.hpp +@@ -0,0 +1,82 @@ ++/* ++ ++Copyright (c) 2017, Arvid Norberg ++All rights reserved. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions ++are met: ++ ++ * Redistributions of source code must retain the above copyright ++ notice, this list of conditions and the following disclaimer. ++ * Redistributions in binary form must reproduce the above copyright ++ notice, this list of conditions and the following disclaimer in ++ the documentation and/or other materials provided with the distribution. ++ * Neither the name of the author nor the names of its ++ contributors may be used to endorse or promote products derived ++ from this software without specific prior written permission. ++ ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++POSSIBILITY OF SUCH DAMAGE. ++ ++*/ ++ ++#ifndef TRY_SIGNAL_POSIX_HPP_INCLUDED ++#define TRY_SIGNAL_POSIX_HPP_INCLUDED ++ ++#include "signal_error_code.hpp" ++#include // for sigjmp_buf ++#include ++ ++namespace sig { ++ ++namespace detail { ++ ++extern std::atomic_flag once; ++ ++struct scoped_jmpbuf ++{ ++ explicit scoped_jmpbuf(sigjmp_buf* ptr); ++ ~scoped_jmpbuf(); ++ scoped_jmpbuf(scoped_jmpbuf const&) = delete; ++ scoped_jmpbuf& operator=(scoped_jmpbuf const&) = delete; ++private: ++ sigjmp_buf* _previous_ptr; ++}; ++ ++void handler(int const signo, siginfo_t* si, void*); ++void setup_handler(); ++ ++} // detail namespace ++ ++template ++void try_signal(Fun&& f) ++{ ++ if (sig::detail::once.test_and_set() == false) { ++ sig::detail::setup_handler(); ++ } ++ ++ sigjmp_buf buf; ++ int const sig = sigsetjmp(buf, 1); ++ // set the thread local jmpbuf pointer, and make sure it's cleared when we ++ // leave the scope ++ sig::detail::scoped_jmpbuf scope(&buf); ++ if (sig != 0) ++ throw std::system_error(static_cast(sig)); ++ ++ f(); ++} ++ ++} ++ ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/CMakeLists.txt +@@ -0,0 +1,61 @@ ++project(libsimulator) ++cmake_minimum_required(VERSION 2.8.7) ++ ++set(SRC_DIR src) ++set(TEST_SRC_DIR test) ++set(INCLUDE_DIR include) ++set(CMAKE_CXX_STANDARD 14) ++set(CMAKE_CXX_STANDARD_REQUIRED ON) ++ ++# Boost.System is header-only (since Boost 1.69), so we only need the Boost ++# headers. Requesting the "system" component fails with recent Boost releases ++# that no longer ship a boost_system CMake config file. ++find_package(Boost REQUIRED) ++find_package(Threads REQUIRED) ++ ++set(SIMULATOR_SRC_FILES ++ ${SRC_DIR}/acceptor.cpp ++ ${SRC_DIR}/high_resolution_timer.cpp ++ ${SRC_DIR}/io_service.cpp ++ ${SRC_DIR}/resolver.cpp ++ ${SRC_DIR}/sink_forwarder.cpp ++ ${SRC_DIR}/udp_socket.cpp ++ ${SRC_DIR}/default_config.cpp ++ ${SRC_DIR}/http_proxy.cpp ++ ${SRC_DIR}/pcap.cpp ++ ${SRC_DIR}/simulation.cpp ++ ${SRC_DIR}/socks_server.cpp ++ ${SRC_DIR}/high_resolution_clock.cpp ++ ${SRC_DIR}/http_server.cpp ++ ${SRC_DIR}/queue.cpp ++ ${SRC_DIR}/simulator.cpp ++ ${SRC_DIR}/tcp_socket.cpp ++ ${SRC_DIR}/nat.cpp ++) ++ ++add_library(simulator ${SIMULATOR_SRC_FILES}) ++target_include_directories(simulator PUBLIC ${INCLUDE_DIR}) ++target_link_libraries(simulator PUBLIC Boost::boost) ++ ++if(WIN32) ++ target_link_libraries(simulator PRIVATE ws2_32) ++endif() ++ ++enable_testing() ++ ++function(define_test) ++ set(NAME ${ARGV0}) ++ add_executable(${NAME} ${TEST_SRC_DIR}/${NAME}.cpp ${TEST_SRC_DIR}/main.cpp) ++ target_link_libraries(${NAME} simulator) ++ target_link_libraries(${NAME} Threads::Threads) ++ add_test(NAME ${NAME} COMMAND ${NAME}) ++endfunction() ++ ++define_test(acceptor) ++define_test(multi_accept) ++define_test(multi_homed) ++define_test(null_buffers) ++define_test(parse_request) ++define_test(resolver) ++define_test(timer) ++define_test(udp_socket) +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/Jamfile +@@ -0,0 +1,136 @@ ++# This Jamfile requires boost-build v2 to build. ++ ++import path ; ++import modules ; ++import os ; ++import testing ; ++ ++BOOST_ROOT = [ modules.peek : BOOST_ROOT ] ; ++ ++ECHO "BOOST_ROOT =" $(BOOST_ROOT) ; ++ECHO "OS =" [ os.name ] ; ++ ++lib wsock32 : : wsock32 shared ; ++lib ws2_32 : : ws2_32 shared ; ++ ++if $(BOOST_ROOT) ++{ ++ use-project /boost : $(BOOST_ROOT) ; ++ alias boost_system : /boost/system//boost_system ; ++} ++else ++{ ++ local boost-lib-search-path = ++ /usr/local/opt/boost/lib ++ /opt/homebrew/lib ++ ; ++ ++ local boost-include-path = ++ /usr/local/opt/boost/include ++ /opt/homebrew/include ++ ; ++ ++ # boost_system is a header-only library now, but we still need to find the boost headers ++ alias boost_system : : : : $(boost-include-path) ; ++} ++ ++SOURCES = ++ simulator ++ simulation ++ io_service ++ high_resolution_timer ++ high_resolution_clock ++ tcp_socket ++ udp_socket ++ queue ++ acceptor ++ default_config ++ http_server ++ socks_server ++ resolver ++ http_proxy ++ sink_forwarder ++ pcap ++ nat ++ ; ++ ++lib simulator ++ : # sources ++ src/$(SOURCES).cpp ++ ++ : # requirements ++ include ++ boost_system ++ windows:ws2_32 ++ windows:wsock32 ++ multi ++ ++ shared:SIMULATOR_BUILDING_SHARED ++ _CRT_SECURE_NO_WARNINGS ++ ++ # https://github.com/chriskohlhoff/asio/issues/290#issuecomment-377727614 ++ _SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING ++ ++ BOOST_ASIO_DISABLE_BOOST_DATE_TIME ++ BOOST_ASIO_HAS_MOVE ++ BOOST_ASIO_ENABLE_CANCELIO ++ ++ # make sure asio uses std::chrono ++ BOOST_ASIO_HAS_STD_CHRONO ++ ++ # io_executor only implements the legacy (Networking TS v1) executor ++ # concept: this falls any_io_executor back to boost::asio::executor, ++ # which it can wrap, so boost::asio::ssl::stream's bookkeeping timers ++ # work for consumers that include simulator/ssl.hpp. ++ BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT ++ ++ # disable auto-link ++ BOOST_ALL_NO_LIB ++ ++ : # default build ++ all ++ on ++ # boost.asio has a global tss_ptr which is the head of a ++ # linked list of all invocations of run on that thread. This ++ # determines whether dispatch() will run the handler immediately ++ # not. The simulator relies on this. On windows each DLL will have ++ # its own copy of this variable, effectively causing a deadlock ++ # in the simulator. So, default to static linking ++ static ++ ++ : # usage requirements ++ BOOST_ASIO_DISABLE_BOOST_DATE_TIME ++ BOOST_ASIO_HAS_MOVE ++ BOOST_ASIO_ENABLE_CANCELIO ++ BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT ++ multi ++ include ++ shared:SIMULATOR_LINKING_SHARED ++ ++ # https://github.com/chriskohlhoff/asio/issues/290#issuecomment-377727614 ++ _SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING ++ ; ++ ++project ++ : requirements ++ simulator ++ # disable auto-link ++ BOOST_ALL_NO_LIB ++ : default-build ++ static ++ multi ++ 14 ++ ; ++ ++test-suite simulator-tests : [ run ++ test/main.cpp ++ test/resolver.cpp ++ test/multi_homed.cpp ++ test/timer.cpp ++ test/acceptor.cpp ++ test/multi_accept.cpp ++ test/null_buffers.cpp ++ test/udp_socket.cpp ++ test/parse_request.cpp ++ ] ; ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/LICENSE +@@ -0,0 +1,675 @@ ++ GNU GENERAL PUBLIC LICENSE ++ Version 3, 29 June 2007 ++ ++ Copyright (C) 2007 Free Software Foundation, Inc. ++ Everyone is permitted to copy and distribute verbatim copies ++ of this license document, but changing it is not allowed. ++ ++ Preamble ++ ++ The GNU General Public License is a free, copyleft license for ++software and other kinds of works. ++ ++ The licenses for most software and other practical works are designed ++to take away your freedom to share and change the works. By contrast, ++the GNU General Public License is intended to guarantee your freedom to ++share and change all versions of a program--to make sure it remains free ++software for all its users. We, the Free Software Foundation, use the ++GNU General Public License for most of our software; it applies also to ++any other work released this way by its authors. You can apply it to ++your programs, too. ++ ++ When we speak of free software, we are referring to freedom, not ++price. Our General Public Licenses are designed to make sure that you ++have the freedom to distribute copies of free software (and charge for ++them if you wish), that you receive source code or can get it if you ++want it, that you can change the software or use pieces of it in new ++free programs, and that you know you can do these things. ++ ++ To protect your rights, we need to prevent others from denying you ++these rights or asking you to surrender the rights. Therefore, you have ++certain responsibilities if you distribute copies of the software, or if ++you modify it: responsibilities to respect the freedom of others. ++ ++ For example, if you distribute copies of such a program, whether ++gratis or for a fee, you must pass on to the recipients the same ++freedoms that you received. You must make sure that they, too, receive ++or can get the source code. And you must show them these terms so they ++know their rights. ++ ++ Developers that use the GNU GPL protect your rights with two steps: ++(1) assert copyright on the software, and (2) offer you this License ++giving you legal permission to copy, distribute and/or modify it. ++ ++ For the developers' and authors' protection, the GPL clearly explains ++that there is no warranty for this free software. For both users' and ++authors' sake, the GPL requires that modified versions be marked as ++changed, so that their problems will not be attributed erroneously to ++authors of previous versions. ++ ++ Some devices are designed to deny users access to install or run ++modified versions of the software inside them, although the manufacturer ++can do so. This is fundamentally incompatible with the aim of ++protecting users' freedom to change the software. The systematic ++pattern of such abuse occurs in the area of products for individuals to ++use, which is precisely where it is most unacceptable. Therefore, we ++have designed this version of the GPL to prohibit the practice for those ++products. If such problems arise substantially in other domains, we ++stand ready to extend this provision to those domains in future versions ++of the GPL, as needed to protect the freedom of users. ++ ++ Finally, every program is threatened constantly by software patents. ++States should not allow patents to restrict development and use of ++software on general-purpose computers, but in those that do, we wish to ++avoid the special danger that patents applied to a free program could ++make it effectively proprietary. To prevent this, the GPL assures that ++patents cannot be used to render the program non-free. ++ ++ The precise terms and conditions for copying, distribution and ++modification follow. ++ ++ TERMS AND CONDITIONS ++ ++ 0. Definitions. ++ ++ "This License" refers to version 3 of the GNU General Public License. ++ ++ "Copyright" also means copyright-like laws that apply to other kinds of ++works, such as semiconductor masks. ++ ++ "The Program" refers to any copyrightable work licensed under this ++License. Each licensee is addressed as "you". "Licensees" and ++"recipients" may be individuals or organizations. ++ ++ To "modify" a work means to copy from or adapt all or part of the work ++in a fashion requiring copyright permission, other than the making of an ++exact copy. The resulting work is called a "modified version" of the ++earlier work or a work "based on" the earlier work. ++ ++ A "covered work" means either the unmodified Program or a work based ++on the Program. ++ ++ To "propagate" a work means to do anything with it that, without ++permission, would make you directly or secondarily liable for ++infringement under applicable copyright law, except executing it on a ++computer or modifying a private copy. Propagation includes copying, ++distribution (with or without modification), making available to the ++public, and in some countries other activities as well. ++ ++ To "convey" a work means any kind of propagation that enables other ++parties to make or receive copies. Mere interaction with a user through ++a computer network, with no transfer of a copy, is not conveying. ++ ++ An interactive user interface displays "Appropriate Legal Notices" ++to the extent that it includes a convenient and prominently visible ++feature that (1) displays an appropriate copyright notice, and (2) ++tells the user that there is no warranty for the work (except to the ++extent that warranties are provided), that licensees may convey the ++work under this License, and how to view a copy of this License. If ++the interface presents a list of user commands or options, such as a ++menu, a prominent item in the list meets this criterion. ++ ++ 1. Source Code. ++ ++ The "source code" for a work means the preferred form of the work ++for making modifications to it. "Object code" means any non-source ++form of a work. ++ ++ A "Standard Interface" means an interface that either is an official ++standard defined by a recognized standards body, or, in the case of ++interfaces specified for a particular programming language, one that ++is widely used among developers working in that language. ++ ++ The "System Libraries" of an executable work include anything, other ++than the work as a whole, that (a) is included in the normal form of ++packaging a Major Component, but which is not part of that Major ++Component, and (b) serves only to enable use of the work with that ++Major Component, or to implement a Standard Interface for which an ++implementation is available to the public in source code form. A ++"Major Component", in this context, means a major essential component ++(kernel, window system, and so on) of the specific operating system ++(if any) on which the executable work runs, or a compiler used to ++produce the work, or an object code interpreter used to run it. ++ ++ The "Corresponding Source" for a work in object code form means all ++the source code needed to generate, install, and (for an executable ++work) run the object code and to modify the work, including scripts to ++control those activities. However, it does not include the work's ++System Libraries, or general-purpose tools or generally available free ++programs which are used unmodified in performing those activities but ++which are not part of the work. For example, Corresponding Source ++includes interface definition files associated with source files for ++the work, and the source code for shared libraries and dynamically ++linked subprograms that the work is specifically designed to require, ++such as by intimate data communication or control flow between those ++subprograms and other parts of the work. ++ ++ The Corresponding Source need not include anything that users ++can regenerate automatically from other parts of the Corresponding ++Source. ++ ++ The Corresponding Source for a work in source code form is that ++same work. ++ ++ 2. Basic Permissions. ++ ++ All rights granted under this License are granted for the term of ++copyright on the Program, and are irrevocable provided the stated ++conditions are met. This License explicitly affirms your unlimited ++permission to run the unmodified Program. The output from running a ++covered work is covered by this License only if the output, given its ++content, constitutes a covered work. This License acknowledges your ++rights of fair use or other equivalent, as provided by copyright law. ++ ++ You may make, run and propagate covered works that you do not ++convey, without conditions so long as your license otherwise remains ++in force. You may convey covered works to others for the sole purpose ++of having them make modifications exclusively for you, or provide you ++with facilities for running those works, provided that you comply with ++the terms of this License in conveying all material for which you do ++not control copyright. Those thus making or running the covered works ++for you must do so exclusively on your behalf, under your direction ++and control, on terms that prohibit them from making any copies of ++your copyrighted material outside their relationship with you. ++ ++ Conveying under any other circumstances is permitted solely under ++the conditions stated below. Sublicensing is not allowed; section 10 ++makes it unnecessary. ++ ++ 3. Protecting Users' Legal Rights From Anti-Circumvention Law. ++ ++ No covered work shall be deemed part of an effective technological ++measure under any applicable law fulfilling obligations under article ++11 of the WIPO copyright treaty adopted on 20 December 1996, or ++similar laws prohibiting or restricting circumvention of such ++measures. ++ ++ When you convey a covered work, you waive any legal power to forbid ++circumvention of technological measures to the extent such circumvention ++is effected by exercising rights under this License with respect to ++the covered work, and you disclaim any intention to limit operation or ++modification of the work as a means of enforcing, against the work's ++users, your or third parties' legal rights to forbid circumvention of ++technological measures. ++ ++ 4. Conveying Verbatim Copies. ++ ++ You may convey verbatim copies of the Program's source code as you ++receive it, in any medium, provided that you conspicuously and ++appropriately publish on each copy an appropriate copyright notice; ++keep intact all notices stating that this License and any ++non-permissive terms added in accord with section 7 apply to the code; ++keep intact all notices of the absence of any warranty; and give all ++recipients a copy of this License along with the Program. ++ ++ You may charge any price or no price for each copy that you convey, ++and you may offer support or warranty protection for a fee. ++ ++ 5. Conveying Modified Source Versions. ++ ++ You may convey a work based on the Program, or the modifications to ++produce it from the Program, in the form of source code under the ++terms of section 4, provided that you also meet all of these conditions: ++ ++ a) The work must carry prominent notices stating that you modified ++ it, and giving a relevant date. ++ ++ b) The work must carry prominent notices stating that it is ++ released under this License and any conditions added under section ++ 7. This requirement modifies the requirement in section 4 to ++ "keep intact all notices". ++ ++ c) You must license the entire work, as a whole, under this ++ License to anyone who comes into possession of a copy. This ++ License will therefore apply, along with any applicable section 7 ++ additional terms, to the whole of the work, and all its parts, ++ regardless of how they are packaged. This License gives no ++ permission to license the work in any other way, but it does not ++ invalidate such permission if you have separately received it. ++ ++ d) If the work has interactive user interfaces, each must display ++ Appropriate Legal Notices; however, if the Program has interactive ++ interfaces that do not display Appropriate Legal Notices, your ++ work need not make them do so. ++ ++ A compilation of a covered work with other separate and independent ++works, which are not by their nature extensions of the covered work, ++and which are not combined with it such as to form a larger program, ++in or on a volume of a storage or distribution medium, is called an ++"aggregate" if the compilation and its resulting copyright are not ++used to limit the access or legal rights of the compilation's users ++beyond what the individual works permit. Inclusion of a covered work ++in an aggregate does not cause this License to apply to the other ++parts of the aggregate. ++ ++ 6. Conveying Non-Source Forms. ++ ++ You may convey a covered work in object code form under the terms ++of sections 4 and 5, provided that you also convey the ++machine-readable Corresponding Source under the terms of this License, ++in one of these ways: ++ ++ a) Convey the object code in, or embodied in, a physical product ++ (including a physical distribution medium), accompanied by the ++ Corresponding Source fixed on a durable physical medium ++ customarily used for software interchange. ++ ++ b) Convey the object code in, or embodied in, a physical product ++ (including a physical distribution medium), accompanied by a ++ written offer, valid for at least three years and valid for as ++ long as you offer spare parts or customer support for that product ++ model, to give anyone who possesses the object code either (1) a ++ copy of the Corresponding Source for all the software in the ++ product that is covered by this License, on a durable physical ++ medium customarily used for software interchange, for a price no ++ more than your reasonable cost of physically performing this ++ conveying of source, or (2) access to copy the ++ Corresponding Source from a network server at no charge. ++ ++ c) Convey individual copies of the object code with a copy of the ++ written offer to provide the Corresponding Source. This ++ alternative is allowed only occasionally and noncommercially, and ++ only if you received the object code with such an offer, in accord ++ with subsection 6b. ++ ++ d) Convey the object code by offering access from a designated ++ place (gratis or for a charge), and offer equivalent access to the ++ Corresponding Source in the same way through the same place at no ++ further charge. You need not require recipients to copy the ++ Corresponding Source along with the object code. If the place to ++ copy the object code is a network server, the Corresponding Source ++ may be on a different server (operated by you or a third party) ++ that supports equivalent copying facilities, provided you maintain ++ clear directions next to the object code saying where to find the ++ Corresponding Source. Regardless of what server hosts the ++ Corresponding Source, you remain obligated to ensure that it is ++ available for as long as needed to satisfy these requirements. ++ ++ e) Convey the object code using peer-to-peer transmission, provided ++ you inform other peers where the object code and Corresponding ++ Source of the work are being offered to the general public at no ++ charge under subsection 6d. ++ ++ A separable portion of the object code, whose source code is excluded ++from the Corresponding Source as a System Library, need not be ++included in conveying the object code work. ++ ++ A "User Product" is either (1) a "consumer product", which means any ++tangible personal property which is normally used for personal, family, ++or household purposes, or (2) anything designed or sold for incorporation ++into a dwelling. In determining whether a product is a consumer product, ++doubtful cases shall be resolved in favor of coverage. For a particular ++product received by a particular user, "normally used" refers to a ++typical or common use of that class of product, regardless of the status ++of the particular user or of the way in which the particular user ++actually uses, or expects or is expected to use, the product. A product ++is a consumer product regardless of whether the product has substantial ++commercial, industrial or non-consumer uses, unless such uses represent ++the only significant mode of use of the product. ++ ++ "Installation Information" for a User Product means any methods, ++procedures, authorization keys, or other information required to install ++and execute modified versions of a covered work in that User Product from ++a modified version of its Corresponding Source. The information must ++suffice to ensure that the continued functioning of the modified object ++code is in no case prevented or interfered with solely because ++modification has been made. ++ ++ If you convey an object code work under this section in, or with, or ++specifically for use in, a User Product, and the conveying occurs as ++part of a transaction in which the right of possession and use of the ++User Product is transferred to the recipient in perpetuity or for a ++fixed term (regardless of how the transaction is characterized), the ++Corresponding Source conveyed under this section must be accompanied ++by the Installation Information. But this requirement does not apply ++if neither you nor any third party retains the ability to install ++modified object code on the User Product (for example, the work has ++been installed in ROM). ++ ++ The requirement to provide Installation Information does not include a ++requirement to continue to provide support service, warranty, or updates ++for a work that has been modified or installed by the recipient, or for ++the User Product in which it has been modified or installed. Access to a ++network may be denied when the modification itself materially and ++adversely affects the operation of the network or violates the rules and ++protocols for communication across the network. ++ ++ Corresponding Source conveyed, and Installation Information provided, ++in accord with this section must be in a format that is publicly ++documented (and with an implementation available to the public in ++source code form), and must require no special password or key for ++unpacking, reading or copying. ++ ++ 7. Additional Terms. ++ ++ "Additional permissions" are terms that supplement the terms of this ++License by making exceptions from one or more of its conditions. ++Additional permissions that are applicable to the entire Program shall ++be treated as though they were included in this License, to the extent ++that they are valid under applicable law. If additional permissions ++apply only to part of the Program, that part may be used separately ++under those permissions, but the entire Program remains governed by ++this License without regard to the additional permissions. ++ ++ When you convey a copy of a covered work, you may at your option ++remove any additional permissions from that copy, or from any part of ++it. (Additional permissions may be written to require their own ++removal in certain cases when you modify the work.) You may place ++additional permissions on material, added by you to a covered work, ++for which you have or can give appropriate copyright permission. ++ ++ Notwithstanding any other provision of this License, for material you ++add to a covered work, you may (if authorized by the copyright holders of ++that material) supplement the terms of this License with terms: ++ ++ a) Disclaiming warranty or limiting liability differently from the ++ terms of sections 15 and 16 of this License; or ++ ++ b) Requiring preservation of specified reasonable legal notices or ++ author attributions in that material or in the Appropriate Legal ++ Notices displayed by works containing it; or ++ ++ c) Prohibiting misrepresentation of the origin of that material, or ++ requiring that modified versions of such material be marked in ++ reasonable ways as different from the original version; or ++ ++ d) Limiting the use for publicity purposes of names of licensors or ++ authors of the material; or ++ ++ e) Declining to grant rights under trademark law for use of some ++ trade names, trademarks, or service marks; or ++ ++ f) Requiring indemnification of licensors and authors of that ++ material by anyone who conveys the material (or modified versions of ++ it) with contractual assumptions of liability to the recipient, for ++ any liability that these contractual assumptions directly impose on ++ those licensors and authors. ++ ++ All other non-permissive additional terms are considered "further ++restrictions" within the meaning of section 10. If the Program as you ++received it, or any part of it, contains a notice stating that it is ++governed by this License along with a term that is a further ++restriction, you may remove that term. If a license document contains ++a further restriction but permits relicensing or conveying under this ++License, you may add to a covered work material governed by the terms ++of that license document, provided that the further restriction does ++not survive such relicensing or conveying. ++ ++ If you add terms to a covered work in accord with this section, you ++must place, in the relevant source files, a statement of the ++additional terms that apply to those files, or a notice indicating ++where to find the applicable terms. ++ ++ Additional terms, permissive or non-permissive, may be stated in the ++form of a separately written license, or stated as exceptions; ++the above requirements apply either way. ++ ++ 8. Termination. ++ ++ You may not propagate or modify a covered work except as expressly ++provided under this License. Any attempt otherwise to propagate or ++modify it is void, and will automatically terminate your rights under ++this License (including any patent licenses granted under the third ++paragraph of section 11). ++ ++ However, if you cease all violation of this License, then your ++license from a particular copyright holder is reinstated (a) ++provisionally, unless and until the copyright holder explicitly and ++finally terminates your license, and (b) permanently, if the copyright ++holder fails to notify you of the violation by some reasonable means ++prior to 60 days after the cessation. ++ ++ Moreover, your license from a particular copyright holder is ++reinstated permanently if the copyright holder notifies you of the ++violation by some reasonable means, this is the first time you have ++received notice of violation of this License (for any work) from that ++copyright holder, and you cure the violation prior to 30 days after ++your receipt of the notice. ++ ++ Termination of your rights under this section does not terminate the ++licenses of parties who have received copies or rights from you under ++this License. If your rights have been terminated and not permanently ++reinstated, you do not qualify to receive new licenses for the same ++material under section 10. ++ ++ 9. Acceptance Not Required for Having Copies. ++ ++ You are not required to accept this License in order to receive or ++run a copy of the Program. Ancillary propagation of a covered work ++occurring solely as a consequence of using peer-to-peer transmission ++to receive a copy likewise does not require acceptance. However, ++nothing other than this License grants you permission to propagate or ++modify any covered work. These actions infringe copyright if you do ++not accept this License. Therefore, by modifying or propagating a ++covered work, you indicate your acceptance of this License to do so. ++ ++ 10. Automatic Licensing of Downstream Recipients. ++ ++ Each time you convey a covered work, the recipient automatically ++receives a license from the original licensors, to run, modify and ++propagate that work, subject to this License. You are not responsible ++for enforcing compliance by third parties with this License. ++ ++ An "entity transaction" is a transaction transferring control of an ++organization, or substantially all assets of one, or subdividing an ++organization, or merging organizations. If propagation of a covered ++work results from an entity transaction, each party to that ++transaction who receives a copy of the work also receives whatever ++licenses to the work the party's predecessor in interest had or could ++give under the previous paragraph, plus a right to possession of the ++Corresponding Source of the work from the predecessor in interest, if ++the predecessor has it or can get it with reasonable efforts. ++ ++ You may not impose any further restrictions on the exercise of the ++rights granted or affirmed under this License. For example, you may ++not impose a license fee, royalty, or other charge for exercise of ++rights granted under this License, and you may not initiate litigation ++(including a cross-claim or counterclaim in a lawsuit) alleging that ++any patent claim is infringed by making, using, selling, offering for ++sale, or importing the Program or any portion of it. ++ ++ 11. Patents. ++ ++ A "contributor" is a copyright holder who authorizes use under this ++License of the Program or a work on which the Program is based. The ++work thus licensed is called the contributor's "contributor version". ++ ++ A contributor's "essential patent claims" are all patent claims ++owned or controlled by the contributor, whether already acquired or ++hereafter acquired, that would be infringed by some manner, permitted ++by this License, of making, using, or selling its contributor version, ++but do not include claims that would be infringed only as a ++consequence of further modification of the contributor version. For ++purposes of this definition, "control" includes the right to grant ++patent sublicenses in a manner consistent with the requirements of ++this License. ++ ++ Each contributor grants you a non-exclusive, worldwide, royalty-free ++patent license under the contributor's essential patent claims, to ++make, use, sell, offer for sale, import and otherwise run, modify and ++propagate the contents of its contributor version. ++ ++ In the following three paragraphs, a "patent license" is any express ++agreement or commitment, however denominated, not to enforce a patent ++(such as an express permission to practice a patent or covenant not to ++sue for patent infringement). To "grant" such a patent license to a ++party means to make such an agreement or commitment not to enforce a ++patent against the party. ++ ++ If you convey a covered work, knowingly relying on a patent license, ++and the Corresponding Source of the work is not available for anyone ++to copy, free of charge and under the terms of this License, through a ++publicly available network server or other readily accessible means, ++then you must either (1) cause the Corresponding Source to be so ++available, or (2) arrange to deprive yourself of the benefit of the ++patent license for this particular work, or (3) arrange, in a manner ++consistent with the requirements of this License, to extend the patent ++license to downstream recipients. "Knowingly relying" means you have ++actual knowledge that, but for the patent license, your conveying the ++covered work in a country, or your recipient's use of the covered work ++in a country, would infringe one or more identifiable patents in that ++country that you have reason to believe are valid. ++ ++ If, pursuant to or in connection with a single transaction or ++arrangement, you convey, or propagate by procuring conveyance of, a ++covered work, and grant a patent license to some of the parties ++receiving the covered work authorizing them to use, propagate, modify ++or convey a specific copy of the covered work, then the patent license ++you grant is automatically extended to all recipients of the covered ++work and works based on it. ++ ++ A patent license is "discriminatory" if it does not include within ++the scope of its coverage, prohibits the exercise of, or is ++conditioned on the non-exercise of one or more of the rights that are ++specifically granted under this License. You may not convey a covered ++work if you are a party to an arrangement with a third party that is ++in the business of distributing software, under which you make payment ++to the third party based on the extent of your activity of conveying ++the work, and under which the third party grants, to any of the ++parties who would receive the covered work from you, a discriminatory ++patent license (a) in connection with copies of the covered work ++conveyed by you (or copies made from those copies), or (b) primarily ++for and in connection with specific products or compilations that ++contain the covered work, unless you entered into that arrangement, ++or that patent license was granted, prior to 28 March 2007. ++ ++ Nothing in this License shall be construed as excluding or limiting ++any implied license or other defenses to infringement that may ++otherwise be available to you under applicable patent law. ++ ++ 12. No Surrender of Others' Freedom. ++ ++ If conditions are imposed on you (whether by court order, agreement or ++otherwise) that contradict the conditions of this License, they do not ++excuse you from the conditions of this License. If you cannot convey a ++covered work so as to satisfy simultaneously your obligations under this ++License and any other pertinent obligations, then as a consequence you may ++not convey it at all. For example, if you agree to terms that obligate you ++to collect a royalty for further conveying from those to whom you convey ++the Program, the only way you could satisfy both those terms and this ++License would be to refrain entirely from conveying the Program. ++ ++ 13. Use with the GNU Affero General Public License. ++ ++ Notwithstanding any other provision of this License, you have ++permission to link or combine any covered work with a work licensed ++under version 3 of the GNU Affero General Public License into a single ++combined work, and to convey the resulting work. The terms of this ++License will continue to apply to the part which is the covered work, ++but the special requirements of the GNU Affero General Public License, ++section 13, concerning interaction through a network will apply to the ++combination as such. ++ ++ 14. Revised Versions of this License. ++ ++ The Free Software Foundation may publish revised and/or new versions of ++the GNU General Public License from time to time. Such new versions will ++be similar in spirit to the present version, but may differ in detail to ++address new problems or concerns. ++ ++ Each version is given a distinguishing version number. If the ++Program specifies that a certain numbered version of the GNU General ++Public License "or any later version" applies to it, you have the ++option of following the terms and conditions either of that numbered ++version or of any later version published by the Free Software ++Foundation. If the Program does not specify a version number of the ++GNU General Public License, you may choose any version ever published ++by the Free Software Foundation. ++ ++ If the Program specifies that a proxy can decide which future ++versions of the GNU General Public License can be used, that proxy's ++public statement of acceptance of a version permanently authorizes you ++to choose that version for the Program. ++ ++ Later license versions may give you additional or different ++permissions. However, no additional obligations are imposed on any ++author or copyright holder as a result of your choosing to follow a ++later version. ++ ++ 15. Disclaimer of Warranty. ++ ++ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY ++APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT ++HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY ++OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ++THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM ++IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ++ALL NECESSARY SERVICING, REPAIR OR CORRECTION. ++ ++ 16. Limitation of Liability. ++ ++ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING ++WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS ++THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY ++GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE ++USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF ++DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD ++PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), ++EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF ++SUCH DAMAGES. ++ ++ 17. Interpretation of Sections 15 and 16. ++ ++ If the disclaimer of warranty and limitation of liability provided ++above cannot be given local legal effect according to their terms, ++reviewing courts shall apply local law that most closely approximates ++an absolute waiver of all civil liability in connection with the ++Program, unless a warranty or assumption of liability accompanies a ++copy of the Program in return for a fee. ++ ++ END OF TERMS AND CONDITIONS ++ ++ How to Apply These Terms to Your New Programs ++ ++ If you develop a new program, and you want it to be of the greatest ++possible use to the public, the best way to achieve this is to make it ++free software which everyone can redistribute and change under these terms. ++ ++ To do so, attach the following notices to the program. It is safest ++to attach them to the start of each source file to most effectively ++state the exclusion of warranty; and each file should have at least ++the "copyright" line and a pointer to where the full notice is found. ++ ++ {one line to give the program's name and a brief idea of what it does.} ++ Copyright (C) {year} {name of author} ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++Also add information on how to contact you by electronic and paper mail. ++ ++ If the program does terminal interaction, make it output a short ++notice like this when it starts in an interactive mode: ++ ++ {project} Copyright (C) {year} {fullname} ++ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. ++ This is free software, and you are welcome to redistribute it ++ under certain conditions; type `show c' for details. ++ ++The hypothetical commands `show w' and `show c' should show the appropriate ++parts of the General Public License. Of course, your program's commands ++might be different; for a GUI interface, you would use an "about box". ++ ++ You should also get your employer (if you work as a programmer) or school, ++if any, to sign a "copyright disclaimer" for the program, if necessary. ++For more information on this, and how to apply and follow the GNU GPL, see ++. ++ ++ The GNU General Public License does not permit incorporating your program ++into proprietary programs. If your program is a subroutine library, you ++may consider it more useful to permit linking proprietary applications with ++the library. If this is what you want to do, use the GNU Lesser General ++Public License instead of this License. But first, please read ++. ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/README.rst +@@ -0,0 +1,199 @@ ++libsimulator ++============ ++ ++.. image:: https://github.com/arvidn/libsimulator/actions/workflows/ci.yml/badge.svg?branch=master ++ :target: https://github.com/arvidn/libsimulator/actions/workflows/ci.yml ++ ++*This is still in initial development, some of this README represents ambitions ++rather than the current state* ++ ++libsimulator is a library for running discrete event simulations, implementing ++the ``boost.asio`` API (or a somewhat faithful emulation of a subset of it, ++patches are welcome). This makes it practical to be used as a testing tool of ++real implementations of network software as well as for writing simulators that ++later turn into live production applications. ++ ++The simulation has to have a single time-line to be deterministic, meaning it ++must be single threaded and use a single ``io_service`` as the message queue. ++These requirements may affect how the program to be tested is written. It may ++for instance require that an external io_service can be provided rather than one ++being wrapped in an internal thread. ++ ++However, ``boost.asio`` programs may generally benefit from being transformed to ++this form, as the become *composable*, i.e. agnostic to which io_service they ++run on or how many threads are running it. ++ ++features ++-------- ++ ++The currently (partially) supported classes are: ++ ++* chrono::high_resolution_clock ++* asio::high_resolution_timer ++* asio::ip::tcp::acceptor ++* asio::ip::tcp::endpoint ++* asio::ip::address (v4 and v6 variants, these just defer to the actual ++ boost.asio types) ++* asio::ip::tcp::socket ++* asio::ip::udp::socket ++* asio::io_service ++* asio::ip::udp::resolver ++* asio::ip::tcp::resolver ++ ++The ``high_resolution_clock`` in the ``chrono`` namespace implements the timer ++concept from the chrono library. ++ ++usage ++----- ++ ++The ``io_service`` object is significantly different from the one in boost.asio. ++This is because one simulation may only have a single message loop and a single ++ordering of events. This single message loop is provided by the ``simulation`` ++class. Each simulation should have only one such object. An ``io_service`` ++object represents a single node on the network. When creating an io_service, you ++have to pass in the simulation it belongs to as well as the IP address it should ++have. It is also possible to pass in multiple addresses to form a multi-homed ++node. For instance, one with both an IPv4 and IPv6 interface. ++ ++When creating sockets, binding and connecting them, the io_service object ++determines what ``INADDR_ANY`` resolves to (the first IP assigned to that node). ++ ++The only aspects of the io_service interface that's preserved are ``post()``, ++``dispatch()`` and constructing timers and sockets. In short, the ``run()`` and ++``poll()`` family of functions do not exist. Every io_service object is assumed ++to be run, and all of their events are handled by the simulation object. ++ ++None of the synchronous APIs are supported, because that would require ++integration with OS threads and scheduler. ++ ++example ++------- ++ ++Here's a simple example illustrating the asio timer:: ++ ++ #include "simulator/simulator.hpp" ++ #include ++ #include ++ ++ void print_time(sim::asio::high_resolution_timer& timer ++ , boost::system::error_code const& ec) ++ { ++ using namespace sim::chrono; ++ static int counter = 0; ++ ++ printf("[%d] timer fired at: %d milliseconds. error: %s\n" ++ , counter ++ , int(duration_cast(high_resolution_clock::now() ++ .time_since_epoch()).count()) ++ , ec.message().c_str()); ++ ++ ++counter; ++ if (counter < 5) ++ { ++ timer.expires_from_now(seconds(counter)); ++ timer.async_wait(std::bind(&print_time, std::ref(timer), _1)); ++ } ++ } ++ ++ int main() ++ { ++ using namespace sim::chrono; ++ ++ default_config cfg; ++ simulation sim(cfg); ++ io_service ios(sim, ip::address_v4::from_string("1.2.3.4")); ++ sim::asio::high_resolution_timer timer(ios); ++ ++ timer.expires_from_now(seconds(1)); ++ timer.async_wait(std::bind(&print_time, std::ref(timer), _1)); ++ ++ boost::system::error_code ec; ++ sim.run(ec); ++ ++ printf("sim::run() returned: %s at: %d\n" ++ , ec.message().c_str() ++ , int(duration_cast(high_resolution_clock::now() ++ .time_since_epoch()).count())); ++ } ++ ++The output from this program is:: ++ ++ [0] timer fired at: 1000 milliseconds. error: Undefined error: 0 ++ [1] timer fired at: 2000 milliseconds. error: Undefined error: 0 ++ [2] timer fired at: 4000 milliseconds. error: Undefined error: 0 ++ [3] timer fired at: 7000 milliseconds. error: Undefined error: 0 ++ [4] timer fired at: 11000 milliseconds. error: Undefined error: 0 ++ io_service::run() returned: Undefined error: 0 at: 11000 ++ ++And obviously it doesn't take 11 wall-clock seconds to run (it returns ++instantly). ++ ++configuration ++------------- ++ ++The simulated network can be configured with per-node pair bandwidth, round-trip ++latency and queue sizes. This is controlled via a callback interface that ++libsimulator will ask for these properties when nodes get connected. ++ ++The resolution of hostnames is also configurable by providing a callback on the ++configuration object along with the latency of individual lookups. ++ ++To configure the network for the simulation, pass in a reference to an object ++implementing the ``sim::configuration`` interface:: ++ ++ struct configuration ++ { ++ // build the network ++ virtual void build(simulation& sim) = 0; ++ ++ // return the hops on the network packets from src to dst need to traverse ++ virtual route channel_route(asio::ip::address src ++ , asio::ip::address dst) = 0; ++ ++ // return the hops an incoming packet to ep need to traverse before ++ // reaching the socket (for instance a NAT) ++ virtual route incoming_route(asio::ip::address ip) = 0; ++ ++ // return the hops an outgoing packet from ep need to traverse before ++ // reaching the network (for instance a DSL modem) ++ virtual route outgoing_route(asio::ip::address ip) = 0; ++ ++ // return the path MTU between the two IP addresses ++ // For TCP sockets, this will be called once when the connection is ++ // established. For UDP sockets it's called for every burst of packets ++ // that are sent ++ virtual int path_mtu(asio::ip::address ip1, asio::ip::address ip2) = 0; ++ ++ // called for every hostname lookup made by the client. ``reqyestor`` is ++ // the node performing the lookup, ``hostname`` is the name being looked ++ // up. Resolve the name into addresses and fill in ``result`` or set ++ // ``ec`` if the hostname is not found or some other error occurs. The ++ // return value is the latency of the lookup. The client's callback won't ++ // be called until after waiting this long. ++ virtual chrono::high_resolution_clock::duration hostname_lookup( ++ asio::ip::address const& requestor ++ , std::string hostname ++ , std::vector& result ++ , boost::system::error_code& ec) = 0; ++ }; ++ ++``build()`` is called right after the simulation is constructed. It gives the ++configuration object an opportunity to construct the core queues, since they ++need access to the simulator. ++ ++``channel_route()`` is expected to return a *route* of network hops from the ++source IP to the destination IP. A route is a series of ``sink`` objects. The ++typical sink is a ``sim::queue``, which is a network node with a specific rate ++limit, propagation delay and queue size. ++ ++*TODO: finish document configuration interface* ++ ++history ++------- ++ ++libsimulator grew out of libtorrent's unit tests, as a tool to make them reliable ++and deterministic (i.e. not depend on external systems like sockets and timers) ++and also easier to debug. The subset of the asio API initially supported by this ++library is the subset used by libtorrent. Patches are welcome to improve ++fidelity and support. ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/chrono.hpp +@@ -0,0 +1,87 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#ifndef CHRONO_HPP_INCLUDED ++#define CHRONO_HPP_INCLUDED ++ ++#include ++#include "simulator/config.hpp" ++ ++#if defined BOOST_ASIO_HAS_STD_CHRONO ++#include ++#else ++#include "simulator/push_warnings.hpp" ++ ++#include ++#include ++#include ++ ++#include "simulator/pop_warnings.hpp" ++#endif ++ ++namespace sim { namespace chrono ++{ ++#if defined BOOST_ASIO_HAS_STD_CHRONO ++ using std::chrono::seconds; ++ using std::chrono::milliseconds; ++ using std::chrono::microseconds; ++ using std::chrono::nanoseconds; ++ using std::chrono::minutes; ++ using std::chrono::hours; ++ using std::chrono::duration_cast; ++ using std::chrono::time_point; ++ using std::chrono::duration; ++#else ++ using boost::chrono::seconds; ++ using boost::chrono::milliseconds; ++ using boost::chrono::microseconds; ++ using boost::chrono::nanoseconds; ++ using boost::chrono::minutes; ++ using boost::chrono::hours; ++ using boost::chrono::duration_cast; ++ using boost::chrono::time_point; ++ using boost::chrono::duration; ++#endif ++ ++ // std.chrono / boost.chrono compatible high_resolution_clock using a simulated time ++ struct SIMULATOR_DECL high_resolution_clock ++ { ++ using rep = std::int64_t; ++#if defined BOOST_ASIO_HAS_STD_CHRONO ++ using period = std::nano; ++ using time_point = std::chrono::time_point; ++ using duration = std::chrono::duration; ++#else ++ using period = boost::nano; ++ using time_point = time_point; ++ using duration = duration; ++#endif ++ static const bool is_steady = true; ++ static time_point now(); ++ ++ // private interface ++ static void fast_forward(high_resolution_clock::duration d); ++ }; ++ ++ SIMULATOR_DECL void reset_clock(); ++ ++} // chrono ++} // sim ++ ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/config.hpp +@@ -0,0 +1,51 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#ifndef CONFIG_HPP_INCLUDED ++#define CONFIG_HPP_INCLUDED ++ ++#include "simulator/push_warnings.hpp" ++#include ++#include "simulator/pop_warnings.hpp" ++ ++#ifdef SIMULATOR_BUILDING_SHARED ++#define SIMULATOR_DECL BOOST_SYMBOL_EXPORT ++#elif defined SIMULATOR_LINKING_SHARED ++#define SIMULATOR_DECL BOOST_SYMBOL_IMPORT ++#else ++#define SIMULATOR_DECL ++#endif ++ ++#if defined __clang__ || defined __GNUC__ ++#define LIBSIMULATOR_NO_RETURN __attribute((noreturn)) ++#elif _MSC_VER ++#define LIBSIMULATOR_NO_RETURN __declspec(noreturn) ++#else ++#define LIBSIMULATOR_NO_RETURN ++#endif ++ ++#ifdef _MSC_VER ++#pragma warning(push) ++// warning C4251: X: class Y needs to have dll-interface to be used by clients of struct ++#pragma warning( disable : 4251) ++// warning C4661: X: no suitable definition provided for explicit template instantiation request ++#pragma warning( disable : 4661) ++#endif ++ ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/function.hpp +@@ -0,0 +1,220 @@ ++/* ++ ++Copyright (c) 2017, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#ifndef SIMULATOR_FUNCTION_HPP_INCLUDED ++#define SIMULATOR_FUNCTION_HPP_INCLUDED ++ ++#include ++#include // for allocator_traits ++ ++#include "simulator/mallocator.hpp" ++ ++namespace sim { ++namespace aux { ++ ++ template ++ T exchange_(T& var, U&& new_val) ++ { ++ T temp = std::move(var); ++ var = std::forward(new_val); ++ return temp; ++ } ++ ++ template ++ T* allocate_handler(Fun h) ++ { ++ using alloc = typename boost::asio::associated_allocator::type>::type; ++ using our_alloc = typename std::allocator_traits::template rebind_alloc; ++ our_alloc al(boost::asio::get_associated_allocator(h)); ++ void* ptr = al.allocate(1); ++ if (ptr == nullptr) throw std::bad_alloc(); ++ try { ++ return new (ptr) T(std::move(h)); ++ } ++ catch (...) { ++ al.deallocate(reinterpret_cast(ptr), 1); ++ throw; ++ } ++ } ++ ++ // this is a std::function-like class that supports move-only function ++ // objects ++ template ++ struct callable ++ { ++ using call_fun_t = R (*)(void*, A&&...); ++ using deallocate_fun_t = void (*)(void*); ++ call_fun_t call_fun; ++ deallocate_fun_t deallocate_fun; ++ }; ++ ++ template ++ R call_impl(void* mem, A&&... a); ++ ++ template ++ void dealloc_impl(void* mem); ++ ++ template ++ struct function_impl : callable ++ { ++ function_impl(Handler h) ++ : handler(std::move(h)) ++ { ++ this->call_fun = call_impl; ++ this->deallocate_fun = dealloc_impl; ++ } ++ Handler handler; ++ }; ++ ++ template ++ R call_impl(void* mem, A&&... a) ++ { ++ auto* obj = static_cast*>(mem); ++ Handler handler = std::move(obj->handler); ++ ++ obj->~function_impl(); ++ using alloc = typename boost::asio::associated_allocator::type; ++ using our_alloc = typename std::allocator_traits:: ++ template rebind_alloc::type>; ++ our_alloc al(boost::asio::get_associated_allocator(handler)); ++ al.deallocate(obj, 1); ++ ++ return handler(std::forward(a)...); ++ } ++ ++ template ++ void dealloc_impl(void* mem) ++ { ++ auto* obj = static_cast*>(mem); ++ Handler h = std::move(obj->handler); ++ ++ obj->~function_impl(); ++ using alloc = typename boost::asio::associated_allocator::type; ++ using our_alloc = typename std::allocator_traits:: ++ template rebind_alloc::type>; ++ our_alloc al(boost::asio::get_associated_allocator(h)); ++ al.deallocate(obj, 1); ++ } ++ ++ template ++ struct function; ++ ++ template ++ struct function ++ { ++ using result_type = R; ++ ++ using allocator_type = aux::mallocator; ++ allocator_type get_allocator() const { return allocator_type{}; } ++ ++ template ++ function(C c) ++ : m_callable(allocate_handler>(std::move(c))) ++ {} ++ function(function&& other) noexcept ++ : m_callable(exchange_(other.m_callable, nullptr)) ++ {} ++ function& operator=(function&& other) noexcept ++ { ++ if (&other == this) return *this; ++ clear(); ++ m_callable = exchange_(other.m_callable, nullptr); ++ return *this; ++ } ++ ++ ~function() { clear(); } ++ ++ // boost.asio requires handlers to be copy-constructible, but it will move ++ // them, if they're movable. So we trick asio into accepting this handler. ++ // If it attempts to copy, it will cause a link error ++ function(function const&) { assert(false && "functions should not be copied"); } ++ function& operator=(function const&) = delete; ++ ++ function() = default; ++ explicit operator bool() const { return m_callable != nullptr; } ++ function& operator=(std::nullptr_t) { clear(); return *this; } ++ void clear() ++ { ++ if (m_callable == nullptr) return; ++ auto fun = m_callable->deallocate_fun; ++ fun(m_callable); ++ m_callable = nullptr; ++ } ++ template ++ R operator()(Args&&... a) ++ { ++ assert(m_callable); ++ auto fun = m_callable->call_fun; ++ return fun(exchange_(m_callable, nullptr), std::forward(a)...); ++ } ++ private: ++ callable* m_callable = nullptr; ++ }; ++ ++ // index sequence, to unpack tuple ++ template struct seq {}; ++ template struct gens : gens {}; ++ template struct gens<0, S...> { using type = seq; }; ++ ++ // a binder for move-only types, and movable arguments. It's not a general ++ // binder as it doesn't support partial application, it just binds all ++ // arguments and ignores any arguments passed to the call ++ template ++ struct move_binder ++ { ++ move_binder(Callable c, A&&... a) ++ : m_args(std::move(a)...) ++ , m_callable(std::move(c)) ++ {} ++ ++ move_binder(move_binder const&) = delete; ++ move_binder& operator=(move_binder const&) = delete; ++ ++ move_binder(move_binder&&) = default; ++ move_binder& operator=(move_binder&&) = default; ++ ++ // ignore any arguments passed in. This is used to ignore an error_code ++ // argument for instance ++ template ++ R operator()(Args...) ++ { ++ return call(typename gens::type()); ++ } ++ ++ private: ++ ++ template ++ R call(seq) ++ { ++ return m_callable(std::move(std::get(m_args))...); ++ } ++ std::tuple m_args; ++ Callable m_callable; ++ }; ++ ++ template ++ move_binder move_bind(C c, A&&... a) ++ { ++ return move_binder(std::move(c), std::forward(a)...); ++ } ++ ++} ++} ++ ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/handler_allocator.hpp +@@ -0,0 +1,81 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#ifndef HANDLER_ALLOCATOR_HPP_INCLUDED ++#define HANDLER_ALLOCATOR_HPP_INCLUDED ++ ++namespace sim ++{ ++namespace aux ++{ ++ ++template ++struct malloc_allocator ++{ ++ using value_type = T; ++ using size_type = std::size_t; ++ ++ friend bool operator==(malloc_allocator, malloc_allocator) { return true; } ++ friend bool operator!=(malloc_allocator, malloc_allocator) { return false; } ++ ++ template ++ struct rebind { using other = malloc_allocator; }; ++ ++ malloc_allocator() = default; ++ template ++ malloc_allocator(malloc_allocator const&) {} ++ ++ T* allocate(std::size_t size) { return static_cast(std::malloc(size * sizeof(T))); } ++ void deallocate(T* pointer, std::size_t) { std::free(pointer); } ++ using is_always_equal = std::true_type; ++}; ++ ++// this is a handler wrapper that customizes the asio handler allocator to use ++// malloc instead of new. The purpose is to distinguish allocations that are ++// internal to the simulator and allocations part of the program under test. ++template ++struct malloc_wrapper ++{ ++ malloc_wrapper(Handler h) : m_handler(std::move(h)) {} ++ ++ template ++ void operator()(Args&&... a) ++ { ++ m_handler(std::forward(a)...); ++ } ++ ++ using allocator_type = malloc_allocator>; ++ ++ allocator_type get_allocator() const noexcept ++ { return allocator_type{}; } ++ ++private: ++ Handler m_handler; ++}; ++ ++template ++malloc_wrapper make_malloc(T h) ++{ ++ return malloc_wrapper(std::move(h)); ++} ++ ++} ++} ++ ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/http_proxy.hpp +@@ -0,0 +1,103 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#ifndef HTTP_PROXY_HPP_INCLUDED ++#define HTTP_PROXY_HPP_INCLUDED ++ ++#include "simulator/simulator.hpp" ++ ++#ifdef _MSC_VER ++#pragma warning(push) ++// warning C4251: X: class Y needs to have dll-interface to be used by clients of struct ++#pragma warning( disable : 4251) ++#endif ++ ++namespace sim ++{ ++ struct http_request; ++ ++// This is a very simple http proxy that only supports a single ++// concurrent connection ++struct SIMULATOR_DECL http_proxy ++{ ++ http_proxy(asio::io_context& ios, unsigned short listen_port); ++ ++ void stop(); ++ ++private: ++ ++ void on_accept(boost::system::error_code const& ec); ++ void on_read_request(boost::system::error_code const& ec, size_t bytes_transferred); ++ ++ void forward_request(http_request const& req); ++ void open_forward_connection(const asio::ip::tcp::endpoint& target); ++ void on_connected(boost::system::error_code const& ec); ++ ++ void on_domain_lookup(boost::system::error_code const& ec ++ , const asio::ip::tcp::resolver::results_type ips); ++ ++ void write_server_send_buffer(); ++ void on_server_write(boost::system::error_code const& ec, size_t bytes_transferred); ++ ++ void on_server_receive(boost::system::error_code const& ec ++ , std::size_t bytes_transferred); ++ void on_server_forward(boost::system::error_code const& ec, size_t bytes_transferred); ++ ++ void error(int code, char const* message); ++ void close_connection(); ++ ++ asio::ip::tcp::resolver m_resolver; ++ asio::ip::tcp::acceptor m_listen_socket; ++ ++ // this is the client connection, i.e. the client connecting to us, sending ++ // HTTP requests that we forward ++ asio::ip::tcp::socket m_client_connection; ++ // client endpoint ++ asio::ip::tcp::endpoint m_ep; ++ ++ // this is the connection to the server the client's requests are forwarded ++ // to ++ asio::ip::tcp::socket m_server_connection; ++ // true while there is an outstanding write operation to the server ++ bool m_writing_to_server; ++ ++ // receive buffer for requests from the client. i.e. client -> proxy (us) -> server ++ char m_client_in_buffer[65536]; ++ // buffer size ++ int m_num_client_in_bytes; ++ ++ char m_server_out_buffer[65536]; ++ int m_num_server_out_bytes; ++ ++ // receive buffer for incoming responses, i.e. server -> proxy (us) -> client ++ char m_in_buffer[65536]; ++ // buffer size ++ int m_num_in_bytes; ++ ++ // set to true when shutting down ++ bool m_close; ++}; ++ ++} ++ ++#ifdef _MSC_VER ++#pragma warning(pop) ++#endif ++ ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/http_server.hpp +@@ -0,0 +1,134 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#ifndef HTTP_SERVER_HPP_INCLUDED ++#define HTTP_SERVER_HPP_INCLUDED ++ ++#include "simulator/simulator.hpp" ++#include ++ ++#ifdef _MSC_VER ++#pragma warning(push) ++// warning C4251: X: class Y needs to have dll-interface to be used by clients of struct ++#pragma warning( disable : 4251) ++#endif ++ ++namespace sim ++{ ++ std::string SIMULATOR_DECL trim(std::string s); ++ ++ std::string SIMULATOR_DECL lower_case(std::string s); ++ ++ std::string SIMULATOR_DECL normalize(const std::string& s); ++ ++ // returns the index to the last byte of the request, or -1 if the buffer ++ // does not contain a full http request ++ int SIMULATOR_DECL find_request_len(char const* buf, int len); ++ ++ struct http_request ++ { ++ std::string method; ++ std::string req; ++ std::string path; ++ std::map headers; ++ }; ++ ++ http_request parse_request(char const* start, int len); ++ ++ // builds an HTTP response buffer ++ std::string SIMULATOR_DECL send_response(int code, char const* status_message ++ , int len = 0, char const** extra_header = NULL); ++ ++// This is a very simple http server that only supports a single concurrent ++// connection ++struct SIMULATOR_DECL http_server ++{ ++ enum flags_t ++ { ++ keep_alive = 1, ++ // behave like an HTTP/1.0 server: respond with an HTTP/1.0 status line ++ // and close the connection after each response. HTTP/1.0 has no ++ // persistent connections, and the Connection header is an HTTP/1.1 ++ // mechanism, so no "Connection: close" is sent -- the client must detect ++ // the close from the protocol version (and the socket closing). ++ http_1_0 = 2 ++ }; ++ ++ http_server(asio::io_context& ios, unsigned short listen_port ++ , int flags = http_server::keep_alive); ++ ++ void stop(); ++ ++ using handler_t = std::function&)>; ++ using generator_t = std::function; ++ ++ void register_handler(std::string const& path, handler_t h); ++ void register_content(std::string const& path ++ , std::int64_t const size, generator_t gen); ++ void register_redirect(std::string const& path, std::string const& target); ++ void register_stall_handler(std::string const& path); ++ ++ // the number of TCP connections that have been accepted so far ++ int accepted_connections() const { return m_accepted_connections; } ++ ++private: ++ ++ void on_accept(boost::system::error_code const& ec); ++ void read(); ++ void on_read(boost::system::error_code const& ec, size_t bytes_transferred); ++ void on_write(boost::system::error_code const& ec, size_t bytes_transferred ++ , bool close); ++ void close_connection(); ++ ++ asio::io_context& m_ios; ++ ++ asio::ip::tcp::acceptor m_listen_socket; ++ ++ asio::ip::tcp::socket m_connection; ++ asio::ip::tcp::endpoint m_ep; ++ ++ std::unordered_map m_handlers; ++ std::set m_stall_handlers; ++ ++ // read buffer, we receive bytes into this buffer for the connection ++ std::string m_recv_buffer; ++ ++ // the number of bytes of m_recv_buffer that we've actually read data into. ++ // The remaining is uninitialized, possibly being read into in an async call ++ int m_bytes_used; ++ ++ std::string m_send_buffer; ++ ++ // set to true when shutting down ++ bool m_close; ++ ++ int m_flags; ++ ++ // counts the number of accepted TCP connections ++ int m_accepted_connections = 0; ++}; ++ ++} ++ ++#ifdef _MSC_VER ++#pragma warning(pop) ++#endif ++ ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/mallocator.hpp +@@ -0,0 +1,69 @@ ++/* ++ ++Copyright (c) 2017, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#ifndef SIMULATOR_MALLOCATOR_HPP_INCLUDED ++#define SIMULATOR_MALLOCATOR_HPP_INCLUDED ++ ++namespace sim ++{ ++namespace aux ++{ ++ struct channel; ++ struct packet; ++ struct pcap; ++ ++ template ++ struct mallocator ++ { ++ template ++ friend struct mallocator; ++ ++ using value_type = T; ++ using pointer = T*; ++ using const_pointer = T const*; ++ using reference = T&; ++ using const_reference = T const&; ++ using size_type = std::size_t; ++ using difference_type = std::ptrdiff_t; ++ ++ template ++ struct rebind { ++ using other = mallocator; ++ }; ++ ++ mallocator() = default; ++ template ++ mallocator(mallocator const&) {} ++ ++ T* allocate(std::size_t size) ++ { return reinterpret_cast(std::malloc(size * sizeof(T))); } ++ ++ void deallocate(T* ptr) { std::free(ptr); } ++ void deallocate(T* ptr, std::size_t) { std::free(ptr); } ++ ++ void destroy(pointer p) { p->~T(); } ++ void construct(pointer p, T&& value) { new (p) T(std::move(value)); } ++ ++ bool operator==(mallocator const&) const { return true; } ++ bool operator!=(mallocator const&) const { return false; } ++ }; ++} // aux ++} // sim ++ ++#endif // SIMULATOR_MALLOCATOR_HPP_INCLUDED ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/nat.hpp +@@ -0,0 +1,51 @@ ++/* ++ ++Copyright (c) 2018, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#ifndef NAT_HPP_INCLUDED ++#define NAT_HPP_INCLUDED ++ ++#include "simulator/sink.hpp" ++#include "simulator/simulator.hpp" ++#include ++ ++namespace sim { ++ ++namespace aux { ++ struct packet; ++} ++ ++ struct SIMULATOR_DECL nat : sink ++ { ++ nat(asio::ip::address external_addr); ++ ~nat() = default; ++ ++ void incoming_packet(aux::packet p) override; ++ ++ // used for visualization ++ std::string label() const override; ++ ++ private: ++ ++ asio::ip::address m_external_addr; ++ }; ++ ++} // sim ++ ++#endif ++ ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/noexcept_movable.hpp +@@ -0,0 +1,56 @@ ++/* ++ ++Copyright (c) 2017, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#ifndef NOEXCEPT_MOVABLE_HPP_INCLUDED ++#define NOEXCEPT_MOVABLE_HPP_INCLUDED ++ ++namespace sim { ++namespace aux { ++ ++ template ++ struct noexcept_movable : T ++ { ++ noexcept_movable() noexcept {} ++ noexcept_movable(noexcept_movable&& rhs) noexcept ++ : T(std::forward(rhs)) ++ {} ++ noexcept_movable(noexcept_movable const& rhs) ++ : T(static_cast(rhs)) ++ {} ++ noexcept_movable(T&& rhs) noexcept : T(std::forward(rhs)) {} // NOLINT ++ noexcept_movable(T const& rhs) : T(rhs) {} // NOLINT ++ noexcept_movable& operator=(noexcept_movable&& rhs) noexcept ++ { ++ this->T::operator=(std::forward(rhs)); ++ return *this; ++ } ++ noexcept_movable& operator=(noexcept_movable const& rhs) ++ { ++ this->T::operator=(rhs); ++ return *this; ++ } ++ ++ using T::T; ++ using T::operator=; ++ }; ++ ++} ++} ++ ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/packet.hpp +@@ -0,0 +1,94 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#ifndef PACKET_HPP_INCLUDED ++#define PACKET_HPP_INCLUDED ++ ++#include "simulator/config.hpp" ++#include "simulator/simulator.hpp" // for route, endpoint ++ ++namespace sim { namespace aux { ++ ++ struct channel; ++ ++ struct packet ++ { ++ packet() = default; ++ ++ // this is move-only ++ packet(packet const&) = delete; ++ packet& operator=(packet const&) = delete; ++ packet(packet&&) = default; ++ packet& operator=(packet&&) = default; ++ ++ // to keep things simple, don't drop ACKs or errors ++ bool ok_to_drop() const ++ { ++ return type != type_t::syn_ack ++ && type != type_t::ack ++ && type != type_t::error; ++ } ++ ++ enum class type_t ++ { ++ uninitialized, // invalid type (used for debugging) ++ syn, // TCP connect ++ syn_ack, // TCP connection accepted ++ ack, // the seq_nr is interpreted as "we received this" ++ error, // the error_code (ec) is set ++ payload // the buffer is filled ++ }; ++ ++ type_t type = type_t::uninitialized; ++ ++ boost::system::error_code ec; ++ ++ // actual payload ++ std::vector buffer; ++ ++ // used for UDP packets ++ asio::ip::udp::endpoint from; ++ ++ // the number of bytes of overhead for this packet. The total packet ++ // size is the number of bytes in the buffer + this number ++ int overhead = 20; ++ ++ // each hop in the route will pop itself off and forward the packet to ++ // the next hop ++ route hops; ++ ++ // for SYN packets, this is set to the channel we're trying to ++ // establish ++ std::shared_ptr channel; ++ ++ // sequence number of this packet (used for debugging) ++ std::uint64_t seq_nr = 0; ++ ++ // the number of (payload) bytes sent over this channel so far. This is ++ // meant to map to the TCP sequence number ++ std::uint32_t byte_counter = 0; ++ ++ // this function must be called with this packet in case the packet is ++ // dropped. ++ aux::function drop_fun; ++ }; ++ ++}} // sim ++ ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/pcap.hpp +@@ -0,0 +1,42 @@ ++/* ++ ++Copyright (c) 2017, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#ifndef PCAP_HPP_INCLUDED ++#define PCAP_HPP_INCLUDED ++ ++#include ++#include "simulator/simulator.hpp" // for endpoint ++ ++namespace sim { namespace aux ++{ ++ struct packet; ++ ++ struct pcap ++ { ++ pcap(char const* filename); ++ void log_tcp(packet const& p, asio::ip::tcp::endpoint src ++ , asio::ip::tcp::endpoint dst); ++ void log_udp(packet const& p, asio::ip::udp::endpoint src ++ , asio::ip::udp::endpoint dst); ++ private: ++ std::fstream m_file; ++ }; ++}} ++ ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/pop_warnings.hpp +@@ -0,0 +1,44 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions ++are met: ++ ++ * Redistributions of source code must retain the above copyright ++ notice, this list of conditions and the following disclaimer. ++ * Redistributions in binary form must reproduce the above copyright ++ notice, this list of conditions and the following disclaimer in ++ the documentation and/or other materials provided with the distribution. ++ * Neither the name of the author nor the names of its ++ contributors may be used to endorse or promote products derived ++ from this software without specific prior written permission. ++ ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++POSSIBILITY OF SUCH DAMAGE. ++ ++*/ ++ ++#ifdef _MSC_VER ++#pragma warning(pop) ++#endif ++ ++#ifdef __clang__ ++#pragma clang diagnostic pop ++#endif ++ ++#ifdef __GNUC__ ++#pragma GCC diagnostic pop ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/push_warnings.hpp +@@ -0,0 +1,89 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions ++are met: ++ ++ * Redistributions of source code must retain the above copyright ++ notice, this list of conditions and the following disclaimer. ++ * Redistributions in binary form must reproduce the above copyright ++ notice, this list of conditions and the following disclaimer in ++ the documentation and/or other materials provided with the distribution. ++ * Neither the name of the author nor the names of its ++ contributors may be used to endorse or promote products derived ++ from this software without specific prior written permission. ++ ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++POSSIBILITY OF SUCH DAMAGE. ++ ++*/ ++ ++#ifdef __GNUC__ ++#pragma GCC diagnostic push ++#pragma GCC diagnostic ignored "-Wall" ++#pragma GCC diagnostic ignored "-Wsign-conversion" ++#pragma GCC diagnostic ignored "-Wconversion" ++#pragma GCC diagnostic ignored "-Wswitch-enum" ++#pragma GCC diagnostic ignored "-Wold-style-cast" ++#pragma GCC diagnostic ignored "-Wundef" ++#pragma GCC diagnostic ignored "-Wmissing-noreturn" ++#pragma GCC diagnostic ignored "-Wdeprecated" ++#pragma GCC diagnostic ignored "-Wdeprecated-declarations" ++#pragma GCC diagnostic ignored "-Wshadow" ++#pragma GCC diagnostic ignored "-Wunused-variable" ++#pragma GCC diagnostic ignored "-Wpedantic" ++#if __GNUC__ >= 6 ++#pragma GCC diagnostic ignored "-Wshift-overflow" ++#pragma GCC diagnostic ignored "-Wshift-count-overflow" ++#pragma GCC diagnostic ignored "-Wshift-count-negative" ++#endif ++#endif ++ ++#ifdef __clang__ ++#pragma clang diagnostic push ++#pragma clang diagnostic ignored "-Wall" ++#pragma clang diagnostic ignored "-Weverything" ++#pragma clang diagnostic ignored "-Wsign-conversion" ++#pragma clang diagnostic ignored "-Wconversion" ++#pragma clang diagnostic ignored "-Wswitch-enum" ++#pragma clang diagnostic ignored "-Wcovered-switch-default" ++#pragma clang diagnostic ignored "-Wold-style-cast" ++#pragma clang diagnostic ignored "-Wundef" ++#pragma clang diagnostic ignored "-Wweak-vtables" ++#pragma clang diagnostic ignored "-Wmissing-noreturn" ++#pragma clang diagnostic ignored "-Wdeprecated" ++#pragma clang diagnostic ignored "-Wdeprecated-declarations" ++#pragma clang diagnostic ignored "-Wcast-align" ++#pragma clang diagnostic ignored "-Wweak-vtable" ++#pragma clang diagnostic ignored "-Wundef" ++#pragma clang diagnostic ignored "-Wshadow" ++#pragma clang diagnostic ignored "-Wimplicit-fallthrough" ++#pragma clang diagnostic ignored "-Wc++11-long-long" ++#pragma clang diagnostic ignored "-Wc++11-extensions" ++#pragma clang diagnostic ignored "-Wextra-semi" ++#pragma clang diagnostic ignored "-Wunused-parameter" ++#pragma clang diagnostic ignored "-Wreserved-id-macro" ++#pragma clang diagnostic ignored "-Wunused-local-typedef" ++#pragma clang diagnostic ignored "-Wgnu-folding-constant" ++#pragma clang diagnostic ignored "-Wdouble-promotion" ++#pragma clang diagnostic ignored "-Wfloat-equal" ++#endif ++ ++#ifdef _MSC_VER ++#pragma warning(push, 1) ++// warning C4005: macro redefinition ++#pragma warning( disable : 4005 ) ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/queue.hpp +@@ -0,0 +1,100 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#ifndef QUEUE_HPP_INCLUDED ++#define QUEUE_HPP_INCLUDED ++ ++#include "simulator/simulator.hpp" ++#include "simulator/packet.hpp" ++#include "simulator/mallocator.hpp" ++ ++#ifdef _MSC_VER ++#pragma warning(push) ++// warning C4251: X: class Y needs to have dll-interface to be used by clients of struct ++#pragma warning( disable : 4251) ++#endif ++ ++namespace sim { ++ ++ struct timed_packet ++ { ++ timed_packet(chrono::high_resolution_clock::time_point t, aux::packet p) ++ : ts(t), pkt(std::move(p)) ++ {} ++ timed_packet(timed_packet&&) = default; ++ timed_packet& operator=(timed_packet&&) = default; ++ timed_packet(timed_packet const&) = delete; ++ timed_packet& operator=(timed_packet const&) = delete; ++ chrono::high_resolution_clock::time_point ts; ++ aux::packet pkt; ++ }; ++ ++ // this is a queue. It can be configured to contrain ++ struct SIMULATOR_DECL queue : sink ++ { ++ queue(asio::io_context& ios, int bandwidth ++ , chrono::high_resolution_clock::duration propagation_delay ++ , int max_queue_size, std::string name = "queue"); ++ ++ virtual void incoming_packet(aux::packet p) override final; ++ ++ virtual std::string label() const override final; ++ ++ queue(queue const&) = delete; ++ queue& operator=(queue const&) = delete; ++ ++ queue(queue&&) = default; ++ queue& operator=(queue&&) = delete; ++ ++ private: ++ ++ void begin_send_next_packet(); ++ void next_packet_sent(); ++ ++ // the queue can't hold more than this number of bytes. Once it's full, ++ // any new packets arriving will be dropped (tail drop) ++ int m_max_queue_size; ++ ++ // the amount of time it takes to forward a packet. Every packet is ++ // delayed by at least this much before being forwarded ++ chrono::high_resolution_clock::duration m_forwarding_latency; ++ ++ // the number of bytes per second that can be sent. This includes the ++ // packet overhead ++ int m_bandwidth; ++ ++ // the number of bytes currently in the packet queue ++ int m_queue_size; ++ ++ std::string m_node_name; ++ ++ // this is the queue of packets and the time each packet was enqueued ++ std::deque> m_queue; ++ asio::high_resolution_timer m_forward_timer; ++ ++ chrono::high_resolution_clock::time_point m_last_forward; ++ }; ++ ++} ++ ++#ifdef _MSC_VER ++#pragma warning(pop) ++#endif ++ ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/simulator.hpp +@@ -0,0 +1,1486 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#ifndef SIMULATOR_HPP_INCLUDED ++#define SIMULATOR_HPP_INCLUDED ++ ++#include "simulator/push_warnings.hpp" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++// only for boost::beast::role_type, used by the teardown()/async_teardown() ++// overloads below (customization points boost::beast::websocket::stream ++// needs for any non-boost::asio socket type it's instantiated over); this ++// is the single lightweight header that defines it, not a dependency on ++// beast's websocket/http machinery. ++#include ++ ++#include ++ ++#include "simulator/pop_warnings.hpp" ++ ++#include "simulator/chrono.hpp" ++#include "simulator/sink_forwarder.hpp" ++#include "simulator/function.hpp" ++#include "simulator/noexcept_movable.hpp" ++#include "simulator/mallocator.hpp" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifndef IP_DONTFRAGMENT ++#define IP_DONTFRAGMENT 1 ++#endif ++ ++namespace sim ++{ ++ namespace aux ++ { ++ struct channel; ++ struct packet; ++ struct pcap; ++ ++ // this is outgoing NIC bandwidth ++ constexpr int nic_bandwidth = 100000000; // 100 MB/s ++ } ++ ++ // this represents a network route (a series of sinks to pass a packet ++ // through) ++ struct SIMULATOR_DECL route ++ { ++ friend route operator+(route lhs, route rhs) ++ { return std::move(lhs.append(std::move(rhs))); } ++ ++ std::shared_ptr next_hop() const { return hops.front(); } ++ std::shared_ptr pop_front() ++ { ++ if (hops.empty()) return std::shared_ptr(); ++ std::shared_ptr ret(std::move(hops.front())); ++ hops.erase(hops.begin()); ++ return ret; ++ } ++ void replace_last(std::shared_ptr s) { hops.back() = std::move(s); } ++ void prepend(route const& r) ++ { hops.insert(hops.begin(), r.hops.begin(), r.hops.end()); } ++ void prepend(std::shared_ptr s) { hops.insert(hops.begin(), std::move(s)); } ++ route& append(route const& r) ++ { hops.insert(hops.end(), r.hops.begin(), r.hops.end()); return *this; } ++ route& append(std::shared_ptr s) { hops.push_back(std::move(s)); return *this; } ++ bool empty() const { return hops.empty(); } ++ std::shared_ptr last() const ++ { return hops.back(); } ++ ++ private: ++ std::deque, aux::mallocator>> hops; ++ }; ++ ++ void forward_packet(aux::packet p); ++ ++ struct simulation; ++ struct configuration; ++ struct queue; ++ ++ namespace asio ++ { ++ ++ using boost::asio::buffer_size; ++ using boost::asio::const_buffer; ++ using boost::asio::mutable_buffer; ++ using boost::asio::buffer; ++ ++ // brought in here (rather than relying on ADL at each call site below) ++ // so buffer sequence types whose associated namespaces don't include ++ // boost::asio (e.g. boost::beast's internal buffer sequence types) are ++ // still found; the "using" also participates in ADL's overload set at ++ // the point of an unqualified call, so this doesn't shadow lookups for ++ // types that do live in boost::asio. ++ using boost::asio::buffer_sequence_begin; ++ using boost::asio::buffer_sequence_end; ++ ++ using boost::asio::post; ++ using boost::asio::dispatch; ++ using boost::asio::defer; ++ ++ struct io_context; ++ ++ struct io_executor ++ { ++ io_executor(io_context& ctx) : m_ctx(&ctx) {} ++ io_context& context() const { return *m_ctx; } ++ ++ template ++ void dispatch(Handler handler, Allocator const& a) const; ++ ++ template ++ void post(Handler handler, Allocator const& a) const; ++ ++ template ++ void defer(Handler handler, Allocator const& a) const; ++ ++ void on_work_finished() const {} ++ void on_work_started() const {} ++ ++ bool running_in_this_thread() const { return true; } ++ ++ friend bool operator==(io_executor const& lhs, io_executor const& rhs) ++ { return lhs.m_ctx == rhs.m_ctx; } ++ ++ friend bool operator!=(io_executor const& lhs, io_executor const& rhs) ++ { return lhs.m_ctx != rhs.m_ctx; } ++ ++ private: ++ io_context* m_ctx; ++ }; ++ ++ struct SIMULATOR_DECL high_resolution_timer ++ { ++ friend struct sim::simulation; ++ ++ using time_type = chrono::high_resolution_clock::time_point; ++ using duration_type = chrono::high_resolution_clock::duration; ++ ++ using executor_type = io_executor; ++ executor_type get_executor(); ++ ++ explicit high_resolution_timer(io_context& io_context); ++ high_resolution_timer(io_context& io_context, ++ const time_type& expiry_time); ++ high_resolution_timer(io_context& io_context, ++ const duration_type& expiry_time); ++ high_resolution_timer(high_resolution_timer&&) noexcept = default; ++ high_resolution_timer& operator=(high_resolution_timer&&) noexcept = default; ++ ~high_resolution_timer(); ++ ++ std::size_t cancel(); ++ std::size_t cancel_one(); ++ ++ time_type expiry() const; ++ std::size_t expires_at(const time_type& expiry_time); ++ std::size_t expires_after(const duration_type& expiry_time); ++ ++ void wait(); ++ void wait(boost::system::error_code& ec); ++ ++ void async_wait(aux::function handler); ++ ++ private: ++ ++ void fire(boost::system::error_code ec); ++ ++ time_type m_expiration_time; ++ aux::function m_handler; ++ io_context* m_io_service; ++ bool m_expired; ++ }; ++ ++ using waitable_timer = high_resolution_timer; ++ ++ namespace error = boost::asio::error; ++ ++ template ++ struct socket_base ++ { ++ socket_base(io_context& ios) : m_io_service(ios) {} ++ socket_base(socket_base&& s) = default; ++ ++ enum wait_type_t ++ { ++ wait_read, wait_write, wait_error ++ }; ++ ++ // alias matching the name used by boost.asio's basic_socket::wait_type, ++ // for code that's written against both APIs ++ using wait_type = wait_type_t; ++ ++ // io_control ++ using reuse_address = boost::asio::socket_base::reuse_address; ++ using executor_type = io_executor; ++ executor_type get_executor(); ++ ++ // socket options ++ using send_buffer_size = boost::asio::socket_base::send_buffer_size; ++ using receive_buffer_size = boost::asio::socket_base::receive_buffer_size; ++ ++ template ++ void set_option(Option const& opt, boost::system::error_code&) ++ { ++ Protocol const p = Protocol::v4(); ++ (void)p; ++#ifdef IP_DONTFRAG ++ if (opt.name(p) == IP_DONTFRAG) ++ m_dont_fragment = *reinterpret_cast(opt.data(p)) != 0; ++#endif ++#ifdef IP_DONTFRAGMENT ++ if (opt.name(p) == IP_DONTFRAGMENT) ++ m_dont_fragment = *reinterpret_cast(opt.data(p)) != 0; ++#endif ++#ifdef IP_MTU_DISCOVER ++ if (opt.name(p) == IP_MTU_DISCOVER) ++ m_dont_fragment = *reinterpret_cast(opt.data(p)) == IP_PMTUDISC_DO; ++#endif ++ } ++ ++ void set_option(receive_buffer_size const& op, boost::system::error_code&) ++ { ++ m_max_receive_queue_size = op.value(); ++ } ++ ++ void set_option(send_buffer_size const& op, boost::system::error_code&) ++ { ++ // this limit is specified in microseconds. Given the line rate of ++ // nic_bandwidth, this is the time it takes to send the specified number ++ // of bytes. ++ m_send_queue_time = chrono::microseconds(std::int64_t(double(op.value()) * 1000000.0 / double(aux::nic_bandwidth))); ++ } ++ ++ void set_option(reuse_address const&, boost::system::error_code&) ++ { ++ // TODO: implement ++ } ++ ++ typename Protocol::endpoint local_endpoint(boost::system::error_code& ec) const ++ { ++ if (!m_open) ++ { ++ ec = error::bad_descriptor; ++ return typename Protocol::endpoint{}; ++ } ++ ++ return m_user_bound_to; ++ } ++ ++ typename Protocol::endpoint local_endpoint() const ++ { ++ boost::system::error_code ec; ++ auto const ret = local_endpoint(ec); ++ if (ec) throw boost::system::system_error(ec); ++ return ret; ++ } ++ ++ typename Protocol::endpoint local_bound_to(boost::system::error_code& ec) const ++ { ++ if (!m_open) ++ { ++ ec = error::bad_descriptor; ++ return typename Protocol::endpoint{}; ++ } ++ ++ return m_bound_to; ++ } ++ ++ typename Protocol::endpoint local_bound_to() const ++ { ++ boost::system::error_code ec; ++ auto const ret = local_bound_to(ec); ++ if (ec) throw boost::system::system_error(ec); ++ return ret; ++ } ++ ++ template ++ void get_option(Option&, boost::system::error_code&) { } ++ ++ void get_option(receive_buffer_size& op, boost::system::error_code&) ++ { ++ op = m_max_receive_queue_size; ++ } ++ ++ template ++ void io_control(IoControl const&, boost::system::error_code&) { } ++ ++ template ++ void io_control(IoControl const&) {} ++ ++ void non_blocking(bool b, boost::system::error_code&) ++ { m_non_blocking = b; } ++ ++ void non_blocking(bool b) ++ { m_non_blocking = b; } ++ ++ bool is_open() const ++ { ++ return m_open; ++ } ++ ++ using message_flags = int; ++ ++ // internal interface ++ ++ route get_incoming_route(); ++ route get_outgoing_route(); ++ ++ protected: ++ ++ io_context& m_io_service; ++ ++ typename Protocol::endpoint m_bound_to; ++ ++ // this is the interface the user requested to bind to (in order to ++ // distinguish the concrete interface it was bound to and INADDR_ANY if ++ // that was requested). We keep this separately to return it as the local ++ // endpoint ++ typename Protocol::endpoint m_user_bound_to; ++ ++ // this is an object implementing the sink interface, forwarding ++ // packets to this socket. If this socket is destructed, this forwarder ++ // is redirected to just drop packets. This is necessary since sinks ++ // must be held by shared_ptr, and socket objects aren't. ++ std::shared_ptr m_forwarder; ++ ++ // whether the socket is open or not ++ bool m_open = false; ++ ++ // true if the socket is set to non-blocking mode ++ bool m_non_blocking = false; ++ ++ // when true, the MTU limit is in effect ++ bool m_dont_fragment = false; ++ ++ // the max size of the incoming queue. This is to emulate the send and ++ // receive buffer. This should also depend on the bandwidth, to not ++ // make the queue size not grow too long in time. ++ int m_max_receive_queue_size = 64 * 1024; ++ ++ // the number of microseconds worth of send buffer this socket has, at ++ // NIC linerate. ++ chrono::microseconds m_send_queue_time{200000}; ++ }; ++ ++ namespace ip { ++ ++ using boost::asio::ip::address; ++ using boost::asio::ip::address_v4; ++ using boost::asio::ip::address_v6; ++ ++ using boost::asio::ip::make_address_v4; ++ using boost::asio::ip::make_address_v6; ++ using boost::asio::ip::make_address; ++ ++ using boost::asio::ip::make_network_v4; ++ ++ template ++ struct basic_endpoint : boost::asio::ip::basic_endpoint ++ { ++ basic_endpoint(ip::address const& addr, unsigned short port) ++ : boost::asio::ip::basic_endpoint(addr, port) {} ++ basic_endpoint() : boost::asio::ip::basic_endpoint() {} ++ }; ++ ++ template ++ struct basic_resolver_entry ++ { ++ using endpoint_type = typename Protocol::endpoint; ++ using protocol_type = Protocol; ++ ++ basic_resolver_entry() {} ++ basic_resolver_entry( ++ endpoint_type const& ep ++ , std::string const& host ++ , std::string const& service) ++ : m_endpoint(ep) ++ , m_host_name(host) ++ , m_service(service) ++ {} ++ ++ endpoint_type endpoint() const { return m_endpoint; } ++ std::string host_name() const { return m_host_name; } ++ operator endpoint_type() const { return m_endpoint; } ++ std::string service_name() const { return m_service; } ++ ++ private: ++ endpoint_type m_endpoint; ++ std::string m_host_name; ++ std::string m_service; ++ }; ++ ++ template ++ struct SIMULATOR_DECL basic_resolver ++ { ++ basic_resolver(io_context& ios); ++ ++ using protocol_type = Protocol; ++ using results_type = std::vector, aux::mallocator>>; ++ ++ void cancel(); ++ ++ void async_resolve(std::string hostname, char const* service ++ , aux::function handler); ++ ++ basic_resolver(basic_resolver&&) noexcept; ++ basic_resolver& operator=(basic_resolver&&) noexcept; ++ basic_resolver(basic_resolver const&) = delete; ++ basic_resolver& operator=(basic_resolver const&) = delete; ++ ++ //TODO: add remaining members ++ ++ private: ++ ++ void on_lookup(boost::system::error_code const& ec); ++ ++ struct result_t ++ { ++ chrono::high_resolution_clock::time_point completion_time; ++ boost::system::error_code err; ++ results_type ips; ++ aux::function handler; ++ ++ result_t( ++ chrono::high_resolution_clock::time_point ct ++ , boost::system::error_code e ++ , results_type ips_ ++ , aux::function h) ++ : completion_time(ct) ++ , err(e) ++ , ips(std::move(ips_)) ++ , handler(std::move(h)) ++ {} ++ ++ result_t(result_t&&) noexcept = default; ++ result_t& operator=(result_t&&) = default; ++ result_t(result_t const&) = delete; ++ result_t& operator=(result_t const&) = delete; ++ }; ++ ++ io_context* m_ios; ++ asio::high_resolution_timer m_timer; ++ using queue_t = aux::noexcept_movable>; ++ ++ queue_t m_queue; ++ }; ++ ++ struct SIMULATOR_DECL udp ++ { ++ static udp v4() { return udp(AF_INET); } ++ static udp v6() { return udp(AF_INET6); } ++ ++ using endpoint = basic_endpoint; ++ ++ struct SIMULATOR_DECL socket : socket_base, sink ++ { ++ using endpoint_type = ip::udp::endpoint; ++ using protocol_type = ip::udp; ++ using lowest_layer_type = socket; ++ ++ socket(io_context& ios); ++ ~socket() override; ++ ++ socket(socket const&) = delete; ++ socket& operator=(socket const&) = delete; ++ socket(socket&&); ++ ++ lowest_layer_type& lowest_layer() { return *this; } ++ ++ void bind(ip::udp::endpoint const& ep ++ , boost::system::error_code& ec); ++ void bind(ip::udp::endpoint const& ep); ++ ++ void close(); ++ void close(boost::system::error_code& ec); ++ ++ void cancel(boost::system::error_code& ec); ++ void cancel(); ++ ++ void open(udp protocol, boost::system::error_code& ec); ++ void open(udp protocol); ++ ++ template ++ std::size_t send_to(ConstBufferSequence const& bufs ++ , udp::endpoint const& destination ++ , socket_base::message_flags flags ++ , boost::system::error_code& ec) ++ { ++ std::vector b(buffer_sequence_begin(bufs) ++ , buffer_sequence_end(bufs)); ++ abort_send_handlers(); ++ return send_to_impl(b, destination, flags, ec); ++ } ++ ++ template ++ std::size_t send_to(ConstBufferSequence const& bufs ++ , udp::endpoint const& destination) ++ { ++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); ++ abort_send_handlers(); ++ boost::system::error_code ec; ++ std::size_t ret = send_to_impl(b, destination, 0, ec); ++ if (ec) throw boost::system::system_error(ec); ++ return ret; ++ } ++ ++ void async_wait(socket_base::wait_type_t w ++ , aux::function handler); ++ ++ template ++ void async_receive(BufferSequence const& bufs ++ , aux::function handler) ++ { ++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); ++ abort_recv_handlers(); ++ ++ async_receive_from_impl(b, nullptr, 0, std::move(handler)); ++ } ++ ++ template ++ void async_receive_from(BufferSequence const& bufs ++ , udp::endpoint& sender ++ , aux::function handler) ++ { ++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); ++ abort_recv_handlers(); ++ ++ async_receive_from_impl(b, &sender, 0, std::move(handler)); ++ } ++ ++ template ++ void async_receive_from(BufferSequence const& bufs ++ , udp::endpoint& sender ++ , socket_base::message_flags flags ++ , aux::function handler) ++ { ++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); ++ abort_recv_handlers(); ++ ++ async_receive_from_impl(b, &sender, flags, std::move(handler)); ++ } ++/* ++ void async_read_from(null_buffers const& ++ , aux::function handler) ++ { ++ abort_recv_handlers(); ++ async_read_some_null_buffers_impl(std::move(handler)); ++ } ++*/ ++ ++ template ++ std::size_t receive_from(BufferSequence const& bufs ++ , udp::endpoint& sender) ++ { ++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); ++ assert(!b.empty()); ++ abort_recv_handlers(); ++ boost::system::error_code ec; ++ std::size_t ret = receive_from_impl(b, &sender, 0, ec); ++ if (ec) throw boost::system::system_error(ec); ++ return ret; ++ } ++ ++ template ++ std::size_t receive_from(BufferSequence const& bufs ++ , udp::endpoint& sender ++ , socket_base::message_flags) ++ { ++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); ++ assert(!b.empty()); ++ abort_recv_handlers(); ++ boost::system::error_code ec; ++ std::size_t ret = receive_from_impl(b, &sender, 0, ec); ++ if (ec) throw boost::system::system_error(ec); ++ return ret; ++ } ++ ++ template ++ std::size_t receive_from(BufferSequence const& bufs ++ , udp::endpoint& sender ++ , socket_base::message_flags ++ , boost::system::error_code& ec) ++ { ++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); ++ assert(!b.empty()); ++ abort_recv_handlers(); ++ return receive_from_impl(b, &sender, 0, ec); ++ } ++ ++ // TODO: support connect and remote_endpoint ++ ++ // internal interface ++ ++ // implements sink ++ virtual void incoming_packet(aux::packet p) override final; ++ virtual std::string label() const override final ++ { return m_bound_to.address().to_string(); } ++ ++ void async_receive_from_impl(std::vector const& bufs ++ , udp::endpoint* sender ++ , socket_base::message_flags flags ++ , aux::function handler); ++ ++ std::size_t receive_from_impl( ++ std::vector const& bufs ++ , udp::endpoint* sender ++ , socket_base::message_flags flags ++ , boost::system::error_code& ec); ++ ++ void async_wait_receive_impl( ++ udp::endpoint* sender ++ , aux::function handler); ++ ++ private: ++ ++ void maybe_wakeup_reader(); ++ void abort_send_handlers(); ++ void abort_recv_handlers(); ++ ++ std::size_t send_to_impl(std::vector const& b ++ , udp::endpoint const& dst, message_flags flags ++ , boost::system::error_code& ec); ++ ++ // this is the next time we'll have an opportunity to send another ++ // outgoing packet. This is used to implement the bandwidth constraints ++ // of channels. This may be in the past, in which case it's OK to send ++ // a packet immediately. ++ chrono::high_resolution_clock::time_point m_next_send; ++ ++ // while we're blocked in an async_write_some operation, this is the ++ // handler that should be called once we're done sending ++ aux::function ++ m_send_handler; ++ aux::function ++ m_wait_send_handler; ++ ++ // if we have an outstanding read on this socket, this is set to the ++ // handler. ++ aux::function ++ m_recv_handler; ++ aux::function ++ m_wait_recv_handler; ++ ++ // if we have an outstanding read operation, this is the buffer to ++ // receive into ++ std::vector m_recv_buffer; ++ ++ // if we have an outstanding receive operation, this may point to an ++ // endpoint to fill in the senders IP in ++ udp::endpoint* m_recv_sender; ++ ++ asio::high_resolution_timer m_recv_timer; ++ asio::high_resolution_timer m_send_timer; ++ ++ // this is the incoming queue of packets for each socket ++ std::list> m_incoming_queue; ++ ++ bool m_recv_null_buffers; ++ ++ // the number of bytes in the incoming packet queue ++ int m_queue_size; ++ ++ // our address family ++ bool m_is_v4; ++ }; ++ ++ using resolver = basic_resolver; ++ ++ int family() const { return m_family; } ++ ++ friend bool operator==(udp const& lhs, udp const& rhs) ++ { return lhs.m_family == rhs.m_family; } ++ ++ friend bool operator!=(udp const& lhs, udp const& rhs) ++ { return lhs.m_family != rhs.m_family; } ++ ++ private: ++ // Construct with a specific family. ++ explicit udp(int protocol_family) ++ : m_family(protocol_family) ++ {} ++ ++ int m_family; ++ ++ }; // udp ++ ++ struct SIMULATOR_DECL tcp ++ { ++ // temporary fix until the resolvers are implemented using our endpoint ++ tcp(boost::asio::ip::tcp p) : m_family(p.family()) {} ++ ++ static tcp v4() { return tcp(AF_INET); } ++ static tcp v6() { return tcp(AF_INET6); } ++ ++ int family() const { return m_family; } ++ ++ using endpoint = basic_endpoint; ++ ++ struct SIMULATOR_DECL socket : socket_base, sink ++ { ++ using endpoint_type = ip::tcp::endpoint; ++ using protocol_type = ip::tcp; ++ using lowest_layer_type = socket; ++ ++ explicit socket(io_context& ios); ++ socket(socket const&) = delete; ++ socket& operator=(socket const&) = delete; ++ socket(socket&&); ++ socket& operator=(socket&&); ++ ++ ~socket() override; ++ ++ void close(); ++ void close(boost::system::error_code& ec); ++ void open(tcp protocol, boost::system::error_code& ec); ++ void open(tcp protocol); ++ void bind(ip::tcp::endpoint const& ep ++ , boost::system::error_code& ec); ++ void bind(ip::tcp::endpoint const& ep); ++ tcp::endpoint remote_endpoint(boost::system::error_code& ec) const; ++ tcp::endpoint remote_endpoint() const; ++ ++ lowest_layer_type& lowest_layer() { return *this; } ++ ++ void async_connect(tcp::endpoint const& target ++ , aux::function h); ++ ++ template ++ void async_write_some(ConstBufferSequence const& bufs ++ , aux::function handler) ++ { ++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); ++ abort_send_handlers(); ++ async_write_some_impl(b, std::move(handler)); ++ } ++ ++ void async_wait(socket_base::wait_type_t const w ++ , aux::function handler) ++ { ++ if (w == socket_base::wait_type_t::wait_write) ++ { ++ abort_send_handlers(); ++ async_wait_write_impl(std::move(handler)); ++ } ++ else if (w == socket_base::wait_type_t::wait_read) ++ { ++ abort_recv_handlers(); ++ async_wait_read_impl(std::move(handler)); ++ } ++ } ++ ++ template ++ std::size_t read_some(BufferSequence const& bufs ++ , boost::system::error_code& ec) ++ { ++ assert(m_non_blocking && "blocking operations not supported"); ++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); ++ return read_some_impl(b, ec); ++ } ++ ++ template ++ std::size_t write_some(ConstBufferSequence const& bufs ++ , boost::system::error_code& ec) ++ { ++ assert(m_non_blocking && "blocking operations not supported"); ++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); ++ return write_some_impl(b, ec); ++ } ++ ++ template ++ void async_read_some(BufferSequence const& bufs ++ , aux::function handler) ++ { ++ std::vector b(buffer_sequence_begin(bufs), buffer_sequence_end(bufs)); ++ abort_recv_handlers(); ++ ++ async_read_some_impl(b, std::move(handler)); ++ } ++ ++ std::size_t available(boost::system::error_code & ec) const; ++ std::size_t available() const; ++ ++ void cancel(boost::system::error_code& ec); ++ void cancel(); ++ ++ using socket_base::set_option; ++ using socket_base::get_option; ++ using socket_base::io_control; ++ ++ // private interface ++ ++ // implements sink ++ virtual void incoming_packet(aux::packet p) override; ++ virtual std::string label() const override final ++ { return m_bound_to.address().to_string(); } ++ ++ void internal_connect(tcp::endpoint const& bind_ip ++ , std::shared_ptr const& c ++ , boost::system::error_code& ec); ++ ++ void abort_send_handlers(); ++ void abort_recv_handlers(); ++ ++ virtual bool internal_is_listening(); ++ protected: ++ ++ void maybe_wakeup_reader(); ++ void maybe_wakeup_writer(); ++ ++ void async_write_some_impl(std::vector const& bufs ++ , aux::function handler); ++ void async_read_some_impl(std::vector const& bufs ++ , aux::function handler); ++ void async_wait_read_impl( ++ aux::function handler); ++ void async_wait_write_impl( ++ aux::function handler); ++ std::size_t write_some_impl(std::vector const& bufs ++ , boost::system::error_code& ec); ++ std::size_t read_some_impl(std::vector const& bufs ++ , boost::system::error_code& ec); ++ ++ void send_packet(aux::packet p); ++ ++ // called when a packet is dropped ++ void packet_dropped(aux::packet p); ++ ++ aux::function m_connect_handler; ++ ++ asio::high_resolution_timer m_connect_timer; ++ ++ // the tcp "packet size" (segment size) ++ // TODO: name this constant! ++ int m_mss = 1475; ++ ++ // while we're blocked in an async_write_some operation, this is the ++ // handler that should be called once we're done sending ++ aux::function m_send_handler; ++ aux::function m_wait_send_handler; ++ ++ std::vector m_send_buffer; ++ ++ // this is the incoming queue of packets for each socket ++ std::list> m_incoming_queue; ++ ++ // the number of bytes in the incoming packet queue ++ int m_queue_size = 0; ++ ++ // if we have an outstanding read on this socket, this is set to the ++ // handler. ++ aux::function m_recv_handler; ++ aux::function m_wait_recv_handler; ++ ++ // if we have an outstanding buffer to receive into, these are them ++ std::vector m_recv_buffer; ++ ++ asio::high_resolution_timer m_recv_timer; ++ ++ // our address family ++ bool m_is_v4 = true; ++ ++ // true if the currently outstanding read operation is for null_buffers ++ bool m_recv_null_buffers = false; ++ ++ // true if the currenly outstanding write operation is for null_buffers ++ bool m_send_null_buffers = false; ++ ++ // if this socket is connected to another endpoint, this object is ++ // shared between both sockets and contain information and state about ++ // the channel. ++ std::shared_ptr m_channel; ++ ++ std::uint64_t m_next_outgoing_seq = 0; ++ std::uint64_t m_next_incoming_seq = 0; ++ ++ // the sequence number of the last dropped packet. We should only cut ++ // the cwnd in half once per round-trip. If a whole window is lost, we ++ // need to only halve it once ++ std::uint64_t m_last_drop_seq = 0; ++ ++ // the current congestion window size (in bytes) ++ int m_cwnd = m_mss * 2; ++ ++ // the number of bytes that have been sent but not ACKed yet ++ int m_bytes_in_flight = 0; ++ ++ // reorder buffer for when packets are dropped ++ std::map, aux::mallocator>> m_reorder_buffer; ++ ++ // the sizes of packets given their sequence number ++ std::unordered_map, std::equal_to, aux::mallocator>> m_outstanding_packet_sizes; ++ ++ // packets to re-send (because they were dropped) ++ std::list> m_outgoing_packets; ++ }; ++ ++ struct SIMULATOR_DECL acceptor : socket ++ { ++ explicit acceptor(io_context& ios); ++ acceptor(acceptor&&); ++ ~acceptor() override; ++ ++ void cancel(boost::system::error_code& ec); ++ void cancel(); ++ ++ void listen(int qs = -1); ++ void listen(int qs, boost::system::error_code& ec); ++ ++ void async_accept(ip::tcp::socket& peer ++ , aux::function h); ++ void async_accept(ip::tcp::socket& peer ++ , ip::tcp::endpoint& peer_endpoint ++ , aux::function h); ++ void async_accept(aux::function h); ++ ++ void close(boost::system::error_code& ec); ++ void close(); ++ ++ // private interface ++ ++ // implements sink ++ virtual void incoming_packet(aux::packet p) override final; ++ virtual bool internal_is_listening() override final; ++ ++ private: ++ // check the incoming connection queue to see if any connection in ++ // there is ready to be accepted and delivered to the user ++ void check_accept_queue(); ++ void do_check_accept_queue(boost::system::error_code const& ec); ++ ++ aux::function m_accept_handler; ++ aux::function m_accept_handler2; ++ ++ // the number of half-open incoming connections this listen socket can ++ // hold. If this is -1, this socket is not yet listening and incoming ++ // connection attempts should be rejected. ++ int m_queue_size_limit; ++ ++ // these are incoming connection attempts. Both half-open and ++ // completely connected. When accepting a connection, this queue is ++ // checked first before waiting for a connection attempt. ++ using incoming_conns_t = std::vector, aux::mallocator>>; ++ incoming_conns_t m_incoming_conns; ++ ++ // for new-style accept, allocate socket in here just to fail early ++ boost::optional m_new_socket; ++ ++ // the socket to accept a connection into ++ tcp::socket* m_accept_into; ++ ++ // the endpoint to write the remote endpoint into when accepting ++ tcp::endpoint* m_remote_endpoint; ++ ++ // non copyable ++ acceptor(acceptor const&); ++ acceptor& operator=(acceptor const&); ++ }; ++ ++ using resolver = basic_resolver; ++ ++ friend bool operator==(tcp const& lhs, tcp const& rhs) ++ { return lhs.m_family == rhs.m_family; } ++ ++ friend bool operator!=(tcp const& lhs, tcp const& rhs) ++ { return lhs.m_family != rhs.m_family; } ++ ++ private: ++ // Construct with a specific family. ++ explicit tcp(int protocol_family) ++ : m_family(protocol_family) ++ {} ++ ++ int m_family; ++ }; ++ ++ extern template struct basic_resolver; ++ extern template struct basic_resolver; ++ ++ // boost::beast looks up a close function for a socket-like type via ++ // unqualified lookup + ADL under the name "beast_close_socket" (see ++ // boost/beast/core/stream_traits.hpp); this satisfies that customization ++ // point for tcp::socket - it's just a free function beast's ADL-based ++ // lookup will find because it lives in tcp::socket's own namespace, so ++ // beast::websocket::stream doesn't need this header to ++ // declare it inside boost::beast itself. ++ inline void beast_close_socket(tcp::socket& s) ++ { ++ boost::system::error_code ec; ++ s.close(ec); ++ } ++ ++ // same idea for the teardown()/async_teardown() customization points ++ // boost::beast::websocket::stream looks up (via ADL, from ++ // boost::beast::websocket) for any Socket type it's instantiated over ++ // that isn't a boost::asio socket -- see ++ // boost/beast/websocket/teardown.hpp. tcp::socket has no half-close/ ++ // non-blocking read-drain support to mirror the real graceful-shutdown ++ // sequence those implement for a real socket, so this just closes it; ++ // that's sufficient for driving deterministic test scenarios, which is ++ // the only thing this socket type is ever used for. ++ inline void teardown(boost::beast::role_type, tcp::socket& s, boost::system::error_code& ec) ++ { ++ s.close(ec); ++ } ++ ++ template ++ void async_teardown(boost::beast::role_type, tcp::socket& s, Handler&& handler) ++ { ++ boost::system::error_code ec; ++ s.close(ec); ++ boost::asio::post(s.get_executor(), std::bind(std::forward(handler), ec)); ++ } ++ ++ } // ip ++ ++ // mirrors (a subset of) boost::asio::async_connect(socket, begin, end, ++ // handler): tries each endpoint in the range in turn, closing and ++ // advancing to the next on failure, until one connects or the range is ++ // exhausted. Calls handler(error_code, Iterator) with the iterator that ++ // succeeded, or the end iterator carrying the last endpoint's error if ++ // all of them failed. Lives directly in sim::asio, alongside the ++ // boost::asio free function it mirrors, rather than in sim::asio::ip, so ++ // that unqualified calls resolve to whichever of the two applies via ++ // ADL (ip::tcp::socket's base, socket_base, is declared in this ++ // namespace, which is what makes that lookup work). ++ template ++ void async_connect(ip::tcp::socket& s, Iterator begin, Iterator end, Handler handler) ++ { ++ struct op : std::enable_shared_from_this ++ { ++ op(ip::tcp::socket& sock, Iterator b, Iterator e, Handler h) ++ : socket(sock), iter(b), end(e), handler(std::move(h)) ++ {} ++ ++ void attempt() ++ { ++ socket.async_connect(*iter, [self = this->shared_from_this()] ++ (boost::system::error_code const& ec) { self->on_connect(ec); }); ++ } ++ ++ void on_connect(boost::system::error_code const& ec) ++ { ++ if (ec && std::next(iter) != end) ++ { ++ boost::system::error_code ignore; ++ socket.close(ignore); ++ ++iter; ++ attempt(); ++ return; ++ } ++ ++ handler(ec, iter); ++ } ++ ++ ip::tcp::socket& socket; ++ Iterator iter; ++ Iterator end; ++ Handler handler; ++ }; ++ ++ if (begin == end) ++ { ++ handler(boost::asio::error::make_error_code(boost::asio::error::not_found), end); ++ return; ++ } ++ ++ std::make_shared(s, begin, end, std::move(handler))->attempt(); ++ } ++ ++ } // asio ++ ++ struct SIMULATOR_DECL simulation ++ { ++ // it calls fire() when a timer fires ++ friend struct high_resolution_timer; ++ ++ simulation(configuration& config); ++ ~simulation(); ++ ++ std::size_t run(); ++ ++ std::size_t poll(boost::system::error_code& ec); ++ std::size_t poll(); ++ ++ std::size_t poll_one(boost::system::error_code& ec); ++ std::size_t poll_one(); ++ ++ void stop(); ++ bool stopped() const; ++ void restart(); ++ // private interface ++ ++ void add_timer(asio::high_resolution_timer* t); ++ void remove_timer(asio::high_resolution_timer* t); ++ ++ boost::asio::io_context& get_internal_service() ++ { return m_service; } ++ ++ asio::io_context& get_io_context() ++ { return *m_internal_ios; } ++ ++ asio::ip::tcp::endpoint bind_socket(asio::ip::tcp::socket* socket ++ , asio::ip::tcp::endpoint ep ++ , boost::system::error_code& ec); ++ void unbind_socket(asio::ip::tcp::socket* socket ++ , asio::ip::tcp::endpoint const& ep); ++ void rebind_socket(asio::ip::tcp::socket* prev, asio::ip::tcp::socket* s, asio::ip::tcp::endpoint ep); ++ ++ asio::ip::udp::endpoint bind_udp_socket(asio::ip::udp::socket* socket ++ , asio::ip::udp::endpoint ep ++ , boost::system::error_code& ec); ++ void unbind_udp_socket(asio::ip::udp::socket* socket ++ , asio::ip::udp::endpoint const& ep); ++ void rebind_udp_socket(asio::ip::udp::socket* socket, asio::ip::udp::endpoint ep); ++ ++ std::shared_ptr internal_connect(asio::ip::tcp::socket* s ++ , asio::ip::tcp::endpoint const& target, boost::system::error_code& ec); ++ ++ route find_udp_socket( ++ asio::ip::udp::socket const& socket ++ , asio::ip::udp::endpoint const& ep); ++ ++ configuration& config() const { return m_config; } ++ ++ void add_io_service(asio::io_context* ios); ++ void remove_io_service(asio::io_context* ios); ++ std::vector get_all_io_services() const; ++ ++ aux::pcap* get_pcap() const { return m_pcap.get(); } ++ void log_pcap(char const* filename); ++ ++ private: ++ struct timer_compare ++ { ++ bool operator()(asio::high_resolution_timer const* lhs ++ , asio::high_resolution_timer const* rhs) const ++ { return lhs->expiry() < rhs->expiry(); } ++ }; ++ ++ configuration& m_config; ++ ++ std::unique_ptr m_pcap; ++ ++ // all non-expired timers ++ std::mutex m_timer_queue_mutex; ++ using timer_queue_t = std::vector>; ++ timer_queue_t m_timer_queue; ++ ++ // these are the io services that represent nodes on the network ++ std::unordered_set, std::equal_to, aux::mallocator> m_nodes; ++ ++ using listen_sockets_t = std::map; ++ using listen_socket_iter_t = listen_sockets_t::iterator; ++ listen_sockets_t m_listen_sockets; ++ ++ using udp_sockets_t = std::map; ++ using udp_socket_iter_t = udp_sockets_t::iterator; ++ udp_sockets_t m_udp_sockets; ++ ++ // used for internal timers. this is a pimpl sine our io_context is ++ // incomplete at this point ++ std::unique_ptr m_internal_ios; ++ ++ // the next port to use for an outgoing connection, where the port is not ++ // specified. We want this to be as unique as possible, to distinguish the ++ // TCP streams. ++ std::uint16_t m_next_bind_port = 2000; ++ ++ bool m_stopped = false; ++ ++ // underlying message queue ++ boost::asio::io_context m_service; ++ }; ++ ++ namespace asio { ++ ++ using boost::asio::async_write; ++ using boost::asio::async_read; ++ ++ // boost.asio compatible io_context class that simulates the network ++ // and time. ++ struct SIMULATOR_DECL io_context : boost::asio::execution_context ++ { ++ io_context(sim::simulation& sim); ++ io_context(sim::simulation& sim, ip::address const& ip); ++ io_context(sim::simulation& sim, std::vector const& ips); ++ io_context(std::size_t threads_hint = 0); ++ ~io_context(); ++ ++ // not copyable and non movable (it's not movable because we currently ++ // keep pointers to the io_context instances in the simulator object) ++ io_context(io_context const&) = delete; ++ io_context(io_context&&) = delete; ++ io_context& operator=(io_context const&) = delete; ++ io_context& operator=(io_context&&) = delete; ++ ++ std::size_t run(boost::system::error_code& ec); ++ std::size_t run(); ++ ++ std::size_t poll(boost::system::error_code& ec); ++ std::size_t poll(); ++ ++ std::size_t poll_one(boost::system::error_code& ec); ++ std::size_t poll_one(); ++ ++ template ++ std::size_t run_for(std::chrono::duration const&) ++ { ++ assert(false); ++ return 0; ++ } ++ ++ template ++ std::size_t poll_one_for(std::chrono::duration const&) ++ { ++ assert(false); ++ return 0; ++ } ++ ++ void stop(); ++ bool stopped() const; ++ void restart(); ++ ++ template ++ void dispatch_impl(Handler handler, Allocator const& a) ++ { get_internal_service().get_executor().dispatch(std::move(handler), a); } ++ ++ template ++ void post_impl(Handler handler, Allocator const& a) ++ { get_internal_service().get_executor().post(std::move(handler), a); } ++ ++ template ++ void defer_impl(Handler handler, Allocator const& a) ++ { get_internal_service().get_executor().defer(std::move(handler), a); } ++ ++ // internal interface ++ boost::asio::io_context& get_internal_service(); ++ ++ void add_timer(high_resolution_timer* t); ++ void remove_timer(high_resolution_timer* t); ++ ++ ip::tcp::endpoint bind_socket(ip::tcp::socket* socket, ip::tcp::endpoint ep ++ , boost::system::error_code& ec); ++ void unbind_socket(ip::tcp::socket* socket ++ , ip::tcp::endpoint const& ep); ++ void rebind_socket(asio::ip::tcp::socket* prev, asio::ip::tcp::socket* s, asio::ip::tcp::endpoint ep); ++ ++ ip::udp::endpoint bind_udp_socket(ip::udp::socket* socket, ip::udp::endpoint ep ++ , boost::system::error_code& ec); ++ void unbind_udp_socket(ip::udp::socket* socket ++ , ip::udp::endpoint const& ep); ++ void rebind_udp_socket(asio::ip::udp::socket* socket, asio::ip::udp::endpoint ep); ++ ++ std::shared_ptr internal_connect(ip::tcp::socket* s ++ , ip::tcp::endpoint const& target, boost::system::error_code& ec); ++ ++ route find_udp_socket(asio::ip::udp::socket const& socket ++ , ip::udp::endpoint const& ep); ++ ++ route const& get_outgoing_route(ip::address ip) const ++ { return m_outgoing_route.find(ip)->second; } ++ ++ route const& get_incoming_route(ip::address ip) const ++ { return m_incoming_route.find(ip)->second; } ++ ++ int get_path_mtu(const asio::ip::address& source, const asio::ip::address& dest) const; ++ std::vector const& get_ips() const { return m_ips; } ++ ++ sim::simulation& sim() { return m_sim; } ++ ++ using executor_type = io_executor; ++ executor_type get_executor() { return executor_type(*this); } ++ ++ private: ++ ++ sim::simulation& m_sim; ++ std::vector m_ips; ++ ++ // these are determined by the configuration. They may include NATs and ++ // DSL modems (queues) ++ std::map m_outgoing_route; ++ std::map m_incoming_route; ++ ++ bool m_stopped = false; ++ }; ++ ++ template ++ void io_executor::dispatch(Handler handler, Allocator const& a) const ++ { m_ctx->dispatch_impl(std::move(handler), a); } ++ ++ template ++ void io_executor::post(Handler handler, Allocator const& a) const ++ { m_ctx->post_impl(std::move(handler), a); } ++ ++ template ++ void io_executor::defer(Handler handler, Allocator const& a) const ++ { m_ctx->defer_impl(std::move(handler), a); } ++ ++ template ++ io_executor ++ socket_base::get_executor() ++ { return io_executor(m_io_service); } ++ ++ template ++ route socket_base::get_incoming_route() ++ { ++ route ret = m_io_service.get_incoming_route(m_bound_to.address()); ++ assert(m_forwarder); ++ ret.append(std::static_pointer_cast(m_forwarder)); ++ return ret; ++ } ++ ++ template ++ route socket_base::get_outgoing_route() ++ { ++ return route(m_io_service.get_outgoing_route(m_bound_to.address())); ++ } ++ ++ } // asio ++ ++ // user supplied configuration of the network to simulate ++ struct SIMULATOR_DECL configuration ++ { ++ virtual ~configuration() {} ++ ++ // build the network ++ virtual void build(simulation& sim) = 0; ++ ++ // return the hops on the network packets from src to dst need to traverse ++ virtual route channel_route(asio::ip::address src ++ , asio::ip::address dst) = 0; ++ ++ // return the hops an incoming packet to ep need to traverse before ++ // reaching the socket (for instance a NAT) ++ virtual route incoming_route(asio::ip::address ip) = 0; ++ ++ // return the hops an outgoing packet from ep need to traverse before ++ // reaching the network (for instance a DSL modem) ++ virtual route outgoing_route(asio::ip::address ip) = 0; ++ ++ // return the path MTU between the two IP addresses ++ // For TCP sockets, this will be called once when the connection is ++ // established. For UDP sockets it's called for every burst of packets ++ // that are sent ++ virtual int path_mtu(asio::ip::address ip1, asio::ip::address ip2) = 0; ++ ++ // called for every hostname lookup made by the client. ``reqyestor`` is ++ // the node performing the lookup, ``hostname`` is the name being looked ++ // up. Resolve the name into addresses and fill in ``result`` or set ++ // ``ec`` if the hostname is not found or some other error occurs. The ++ // return value is the latency of the lookup. The client's callback won't ++ // be called until after waiting this long. ++ virtual chrono::high_resolution_clock::duration hostname_lookup( ++ asio::ip::address const& requestor ++ , std::string hostname ++ , std::vector& result ++ , boost::system::error_code& ec) = 0; ++ ++ virtual void clear() = 0; ++ }; ++ ++ struct SIMULATOR_DECL default_config : configuration ++ { ++ default_config() : m_sim(nullptr) {} ++ ++ void build(simulation& sim) override; ++ route channel_route(asio::ip::address src, asio::ip::address dst) override; ++ route incoming_route(asio::ip::address ip) override; ++ route outgoing_route(asio::ip::address ip) override; ++ int path_mtu(asio::ip::address ip1, asio::ip::address ip2) override; ++ chrono::high_resolution_clock::duration hostname_lookup( ++ asio::ip::address const& requestor ++ , std::string hostname ++ , std::vector& result ++ , boost::system::error_code& ec) override; ++ ++ void clear() override; ++ protected: ++ std::shared_ptr m_network; ++ std::map> m_incoming; ++ std::map> m_outgoing; ++ simulation* m_sim; ++ }; ++ ++ namespace aux ++ { ++ /* the channel can be in the following states: ++ 1. handshake-1 - the initiating socket has sent SYN ++ 2. handshake-2 - the accepting connection has sent SYN+ACK ++ 3. handshake-3 - the initiating connection has received the SYN+ACK and ++ considers the connection open, but the 3rd handshake ++ message is still in flight. ++ 4. connected - the accepting side has received the 3rd handshake ++ packet and considers it open ++ ++ Whenever a connection attempt is made to a listening socket, as long as ++ there is still space in the incoming socket queue, the accepting side ++ will always respond immediately and complete the handshake, then wait ++ until the user calls async_accept (which in this case would complete ++ immediately). ++ */ ++ struct SIMULATOR_DECL channel ++ { ++ channel() {} ++ // index 0 is the incoming route to the socket that initiated the connection. ++ // index 1 may be empty while the connection is half-open ++ route hops[2]; ++ ++ // the actual endpoint of each end of the channel ++ asio::ip::tcp::endpoint ep[2]; ++ ++ // observable endpoint of each side of the channel. This is not how you ++ // see yourself, just the other end ++ asio::ip::tcp::endpoint visible_ep[2]; ++ ++ // the number of bytes sent from respective direction ++ // this is used to simulate the TCP sequence number, so it deliberately ++ // is meant to wrap at 32 bits ++ std::uint32_t bytes_sent[2]; ++ ++ int remote_idx(asio::ip::tcp::endpoint const& self) const; ++ int self_idx(asio::ip::tcp::endpoint const& self) const; ++ }; ++ ++ } // aux ++ ++ void SIMULATOR_DECL dump_network_graph(simulation const& s, const std::string& filename); ++} ++ ++#ifdef _MSC_VER ++#pragma warning(pop) ++#endif ++ ++#endif // SIMULATOR_HPP_INCLUDED ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/sink.hpp +@@ -0,0 +1,47 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#ifndef SINK_HPP_INCLUDED ++#define SINK_HPP_INCLUDED ++ ++#include "simulator/config.hpp" ++#include ++ ++namespace sim { ++ ++namespace aux { ++ struct packet; ++} ++ ++ // this is an interface for somthing that can accept incoming packets, ++ // such as queues, sockets, NATs and TCP congestion windows ++ struct SIMULATOR_DECL sink ++ { ++ virtual void incoming_packet(aux::packet p) = 0; ++ ++ // used for visualization ++ virtual std::string label() const = 0; ++ ++ virtual std::string attributes() const { return "shape=box"; } ++ virtual ~sink() = default; ++ }; ++ ++} // sim ++ ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/sink_forwarder.hpp +@@ -0,0 +1,43 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#ifndef SINK_FORWARDER_HPP_INCLUDED ++#define SINK_FORWARDER_HPP_INCLUDED ++ ++#include "simulator/config.hpp" ++#include "simulator/sink.hpp" ++ ++namespace sim { namespace aux { ++ ++ struct packet; ++ ++ struct SIMULATOR_DECL sink_forwarder final : sink ++ { ++ sink_forwarder(sink* dst); ++ void incoming_packet(packet p) override; ++ std::string label() const override; ++ void reset(sink* s = nullptr); ++ ++ private: ++ sink* m_dst; ++ }; ++ ++}} // sim ++ ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/socks_server.hpp +@@ -0,0 +1,202 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#ifndef SOCKS_SERVER_HPP_INCLUDED ++#define SOCKS_SERVER_HPP_INCLUDED ++ ++#include "simulator/simulator.hpp" ++ ++#ifdef __GNUC__ ++#pragma GCC diagnostic push ++#if __GNUC__ >= 9 ++#pragma GCC diagnostic ignored "-Wparentheses" ++#endif ++#endif ++ ++#include ++ ++#ifdef __GNUC__ ++#pragma GCC diagnostic pop ++#endif ++ ++#ifdef _MSC_VER ++#pragma warning(push) ++// warning C4251: X: class Y needs to have dll-interface to be used by clients of struct ++#pragma warning( disable : 4251) ++#endif ++ ++namespace sim ++{ ++ ++enum socks_flag ++{ ++ // when this flag is set, the proxy will close the client connection ++ // immediately after sending the response to a UDP ASSOCIATE command ++ disconnect_udp_associate = 1, ++ ++ // when this flag is set, the reponse to UDP ASSOCIATE will contain an empty ++ // hostname, rather than the relay IP address ++ udp_associate_respond_empty_hostname = 2 ++}; ++ ++struct SIMULATOR_DECL socks_connection : std::enable_shared_from_this ++{ ++ socks_connection(asio::io_context& ios, int version, std::array& cmd_counts ++ , std::uint32_t flags, int& bind_port); ++ ++ asio::ip::tcp::socket& socket() { return m_client_connection; } ++ ++ void start(); ++ ++private: ++ ++ void on_handshake1(boost::system::error_code const& ec, size_t bytes_transferred); ++ void on_handshake2(boost::system::error_code const& ec, size_t bytes_transferred); ++ void on_handshake3(boost::system::error_code const& ec, size_t bytes_transferred); ++ void on_request1(boost::system::error_code const& ec, size_t bytes_transferred); ++ void on_request2(boost::system::error_code const& ec, size_t bytes_transferred); ++ ++ void on_write(boost::system::error_code const& ec, size_t bytes_transferred ++ , bool close); ++ void close_connection(); ++ ++ int format_response(asio::ip::address const& addr, int port, int response); ++ int format_hostname_response(char const* hostname, int port, int response); ++ ++ void on_connected(boost::system::error_code const& ec); ++ void on_request_domain_name(boost::system::error_code const& ec, size_t bytes_transferred); ++ void on_request_domain_lookup(boost::system::error_code const& ec ++ , const asio::ip::tcp::resolver::results_type ips); ++ ++ void open_forward_connection(asio::ip::tcp::endpoint const& target); ++ void bind_connection(asio::ip::tcp::endpoint const& target); ++ void start_accept(boost::system::error_code const& ec); ++ ++ void udp_associate(asio::ip::tcp::endpoint const& target); ++ void on_read_udp(boost::system::error_code const& ec, std::size_t bytes_transferred); ++ void wait_for_eof(boost::system::error_code const& ec, std::size_t bytes_transferred); ++ ++ void on_server_receive(boost::system::error_code const& ec ++ , std::size_t bytes_transferred); ++ void on_server_forward(boost::system::error_code const& ec ++ , size_t bytes_transferred); ++ ++ void on_client_receive(boost::system::error_code const& ec ++ , std::size_t bytes_transferred); ++ void on_client_forward(boost::system::error_code const& ec ++ , size_t bytes_transferred); ++ ++ char const* command() const; ++ ++ int& m_bind_port; ++ ++ asio::io_context& m_ios; ++ ++ asio::ip::udp::resolver m_udp_resolver; ++ ++ asio::ip::tcp::resolver m_resolver; ++ ++ boost::bimap m_name_mapping; ++ ++ // this is the SOCKS client connection, i.e. the client connecting to us and ++ // being forwarded ++ asio::ip::tcp::socket m_client_connection; ++ ++ // this is the connection to the server the socks client is being forwarded ++ // to ++ asio::ip::tcp::socket m_server_connection; ++ asio::ip::tcp::acceptor m_bind_socket; ++ ++ asio::ip::udp::socket m_udp_associate; ++ asio::ip::udp::endpoint m_udp_associate_ep; ++ asio::ip::udp::endpoint m_udp_from; ++ ++ std::array m_udp_buffer; ++ ++ // receive buffer for data going out, i.e. client -> proxy (us) -> server ++ char m_out_buffer[65536]; ++ // buffer size ++ int m_num_out_bytes; ++ ++ // receive buffer for data coming in, i.e. server -> proxy (us) -> client ++ char m_in_buffer[65536]; ++ // buffer size ++ int m_num_in_bytes; ++ ++ // set to true when shutting down ++ bool m_close = false; ++ ++ // the SOCKS protocol version (4 or 5) ++ const int m_version; ++ ++ int m_command; ++ ++ std::array& m_cmd_counts; ++ ++ std::uint32_t const m_flags; ++}; ++ ++// This is a very simple socks4 and 5 server that only supports a single ++// concurrent connection ++struct SIMULATOR_DECL socks_server ++{ ++ socks_server(asio::io_context& ios, unsigned short listen_port ++ , int version = 5, std::uint32_t flags = 0); ++ ++ void stop(); ++ ++ void bind_start_port(int const port) { m_bind_port = port; } ++ ++ // return the number of CONNECT, BIND and UDP_ASSOCIATE commands the proxy ++ // has received ++ std::array cmd_counts() const ++ { return m_cmd_counts; } ++ ++private: ++ ++ void on_accept(boost::system::error_code const& ec); ++ ++ asio::io_context& m_ios; ++ ++ asio::ip::tcp::acceptor m_listen_socket; ++ ++ std::shared_ptr m_conn; ++ ++ asio::ip::tcp::endpoint m_ep; ++ ++ int m_bind_port = 2048; ++ ++ // set to true when shutting down ++ bool m_close = false; ++ ++ // the SOCKS protocol version (4 or 5) ++ const int m_version; ++ ++ std::array m_cmd_counts; ++ ++ std::uint32_t const m_flags; ++}; ++ ++} ++ ++#ifdef _MSC_VER ++#pragma warning(pop) ++#endif ++ ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/ssl.hpp +@@ -0,0 +1,44 @@ ++/* ++ ++Copyright (c) 2026, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#ifndef SIMULATOR_SSL_HPP_INCLUDED ++#define SIMULATOR_SSL_HPP_INCLUDED ++ ++#include "simulator/simulator.hpp" ++ ++#include "simulator/push_warnings.hpp" ++#include ++#include "simulator/pop_warnings.hpp" ++ ++namespace sim { ++namespace asio { ++ ++namespace ssl ++{ ++ using boost::asio::ssl::context; ++ using boost::asio::ssl::stream_base; ++ using boost::asio::ssl::verify_context; ++ ++ template ++ using stream = boost::asio::ssl::stream; ++} // ssl ++ ++} // asio ++} // sim ++ ++#endif +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/include/simulator/utils.hpp +@@ -0,0 +1,47 @@ ++/* ++ ++Copyright (c) 2016, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#ifndef UTILS_HPP_INCLUDED ++#define UTILS_HPP_INCLUDED ++ ++#include "simulator/simulator.hpp" ++ ++namespace sim ++{ ++ ++// shortcut for creating a timer with a timeout and action ++struct timer ++{ ++ timer(simulation& sim, chrono::high_resolution_clock::duration timeout ++ , aux::function&& f) ++ : m_ios(sim, asio::ip::address_v4()) ++ , m_timer(m_ios) ++ { ++ m_timer.expires_after(timeout); ++ m_timer.async_wait(std::move(f)); ++ } ++ ++private: ++ sim::asio::io_context m_ios; ++ sim::asio::high_resolution_timer m_timer; ++}; ++ ++} // sim ++ ++#endif ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/acceptor.cpp +@@ -0,0 +1,379 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#include "simulator/simulator.hpp" ++#include "simulator/packet.hpp" ++ ++#include ++#include ++ ++#include ++ ++using boost::beast::bind_handler; ++ ++typedef sim::chrono::high_resolution_clock::time_point time_point; ++typedef sim::chrono::high_resolution_clock::duration duration; ++ ++namespace sim { ++namespace asio { ++namespace ip { ++ ++ tcp::acceptor::acceptor(io_context& ios) ++ : socket(ios) ++ , m_queue_size_limit(-1) ++ {} ++ ++ tcp::acceptor::acceptor(acceptor&&) = default; ++ ++ tcp::acceptor::~acceptor() ++ { ++ boost::system::error_code ec; ++ close(ec); ++ } ++ ++ void tcp::acceptor::listen(int qs) ++ { ++ boost::system::error_code ec; ++ listen(qs, ec); ++ if (ec) throw boost::system::system_error(ec); ++ } ++ ++ void tcp::acceptor::listen(int qs, boost::system::error_code& ec) ++ { ++ if (qs == -1) qs = 20; ++ ++ if (!m_open) ++ { ++ ec = error::bad_descriptor; ++ return; ++ } ++ if (m_bound_to == ip::tcp::endpoint()) ++ { ++ ec = error::invalid_argument; ++ return; ++ } ++ ++ m_queue_size_limit = qs; ++ ec.clear(); ++ } ++ ++ void tcp::acceptor::close(boost::system::error_code& ec) ++ { ++ m_queue_size_limit = -1; ++ cancel(ec); ++ socket::close(ec); ++ } ++ ++ void tcp::acceptor::close() ++ { ++ if (m_accept_handler) ++ { ++ post(m_io_service, bind_handler(std::exchange(m_accept_handler, nullptr) ++ , boost::system::error_code(error::operation_aborted))); ++ } ++ if (m_accept_handler2) ++ { ++ post(m_io_service, [&, h = std::exchange(m_accept_handler2, nullptr)] () mutable { ++ h(boost::system::error_code(error::operation_aborted) ++ , ip::tcp::socket(m_io_service)); ++ }); ++ } ++ } ++ ++ void tcp::acceptor::cancel(boost::system::error_code& ec) ++ { ++ ec.clear(); ++ if (m_accept_handler) ++ { ++ try ++ { ++ post(m_io_service, bind_handler(std::exchange(m_accept_handler, nullptr) ++ , boost::system::error_code(error::operation_aborted))); ++ } ++ catch (std::bad_alloc const&) ++ { ++ ec = error::no_memory; ++ } ++ catch (std::exception const&) ++ { ++ ec = error::no_memory; ++ } ++ } ++ if (m_accept_handler2) ++ { ++ try ++ { ++ post(m_io_service, [&, h = std::exchange(m_accept_handler2, nullptr)] () mutable { ++ h(boost::system::error_code(error::operation_aborted) ++ , ip::tcp::socket(m_io_service)); ++ }); ++ } ++ catch (std::bad_alloc const&) ++ { ++ ec = error::no_memory; ++ } ++ catch (std::exception const&) ++ { ++ ec = error::no_memory; ++ } ++ } ++ } ++ ++ void tcp::acceptor::cancel() ++ { ++ boost::system::error_code ec; ++ cancel(ec); ++ if (ec) throw boost::system::system_error(ec); ++ } ++ ++ void tcp::acceptor::async_accept(ip::tcp::socket& peer ++ , aux::function h) ++ { ++ // TODO: assert that the io_context we use is the same as the one peer use ++ if (peer.is_open()) ++ { ++ boost::system::error_code ec; ++ peer.close(ec); ++ } ++ ++ if (m_accept_handler) ++ { ++ m_accept_into = nullptr; ++ m_remote_endpoint = nullptr; ++ post(m_io_service, bind_handler(std::exchange(m_accept_handler, nullptr) ++ , boost::system::error_code(error::operation_aborted))); ++ } ++ if (m_accept_handler2) ++ { ++ m_accept_into = nullptr; ++ m_remote_endpoint = nullptr; ++ post(m_io_service, [&, h = std::exchange(m_accept_handler2, nullptr)] () mutable { ++ h(boost::system::error_code(error::operation_aborted) ++ , ip::tcp::socket(m_io_service)); ++ }); ++ } ++ m_accept_handler = std::move(h); ++ m_accept_into = &peer; ++ m_remote_endpoint = nullptr; ++ ++ check_accept_queue(); ++ } ++ ++ void tcp::acceptor::async_accept(ip::tcp::socket& peer ++ , ip::tcp::endpoint& peer_endpoint ++ , aux::function h) ++ { ++ if (peer.is_open()) ++ { ++ boost::system::error_code ec; ++ peer.close(ec); ++ } ++ ++ if (m_accept_handler) ++ { ++ post(m_io_service, bind_handler(std::exchange(m_accept_handler, nullptr) ++ , boost::system::error_code(error::operation_aborted))); ++ } ++ if (m_accept_handler2) ++ { ++ post(m_io_service, [&, h = std::exchange(m_accept_handler2, nullptr)] () mutable { ++ h(boost::system::error_code(error::operation_aborted) ++ , ip::tcp::socket(m_io_service)); ++ }); ++ } ++ m_accept_handler = std::move(h); ++ m_accept_into = &peer; ++ m_remote_endpoint = &peer_endpoint; ++ ++ check_accept_queue(); ++ } ++ ++ void tcp::acceptor::async_accept(aux::function h) ++ { ++ m_remote_endpoint = nullptr; ++ if (m_accept_handler) ++ { ++ m_accept_into = nullptr; ++ post(m_io_service, bind_handler(std::exchange(m_accept_handler, nullptr) ++ , boost::system::error_code(error::operation_aborted))); ++ } ++ if (m_accept_handler2) ++ { ++ m_accept_into = nullptr; ++ post(m_io_service, [&, h = std::exchange(m_accept_handler2, nullptr)] () mutable { ++ h(boost::system::error_code(error::operation_aborted) ++ , ip::tcp::socket(m_io_service)); ++ }); ++ } ++ m_new_socket.emplace(m_io_service); ++ m_accept_handler2 = std::move(h); ++ m_accept_into = &*m_new_socket; ++ ++ check_accept_queue(); ++ } ++ ++ void tcp::acceptor::do_check_accept_queue(boost::system::error_code const& ec) ++ { ++ if (ec) return; ++ check_accept_queue(); ++ } ++ ++ void tcp::acceptor::incoming_packet(aux::packet p) ++ { ++ switch (p.type) ++ { ++ case aux::packet::type_t::syn: ++ m_incoming_conns.push_back(p.channel); ++ check_accept_queue(); ++ return; ++ case aux::packet::type_t::error: ++ assert(false); // something is not wired up correctly ++ if (m_accept_handler) ++ { ++ m_accept_into = nullptr; ++ m_remote_endpoint = nullptr; ++ post(m_io_service, bind_handler(std::exchange(m_accept_handler, nullptr) ++ , boost::system::error_code(error::operation_aborted))); ++ } ++ if (m_accept_handler2) ++ { ++ m_accept_into = nullptr; ++ m_remote_endpoint = nullptr; ++ post(m_io_service, [&, h = std::exchange(m_accept_handler2, nullptr)] () mutable { ++ h(boost::system::error_code(error::operation_aborted) ++ , ip::tcp::socket(m_io_service)); ++ }); ++ } ++ return; ++ default: ++ // if this happens, it implies that an incoming connection sent ++ // payload before receiving a syn_ack. Alternatively that the ++ // acceptor sent the syn_ack but still left the last-hop in the ++ // incoming route to point to this socket, instead of the ++ // accepted-into socket ++ assert(false); ++ return; ++ } ++ } ++ ++ void tcp::acceptor::check_accept_queue() ++ { ++ if (!is_open()) ++ { ++ // if the acceptor socket is closed. Any potential socket in the queue ++ // should be closed too. ++ for (auto const& incoming : m_incoming_conns) ++ { ++ aux::packet p; ++ p.from = asio::ip::udp::endpoint( ++ m_bound_to.address(), m_bound_to.port()); ++ p.type = aux::packet::type_t::error; ++ p.ec = boost::system::error_code(error::connection_reset); ++ p.overhead = 28; ++ p.hops = incoming->hops[0]; ++ ++ forward_packet(std::move(p)); ++ } ++ m_incoming_conns.clear(); ++ ++ if (m_accept_handler) ++ { ++ m_accept_into = nullptr; ++ m_remote_endpoint = nullptr; ++ post(m_io_service, bind_handler(std::exchange(m_accept_handler, nullptr) ++ , boost::system::error_code(error::operation_aborted))); ++ } ++ if (m_accept_handler2) ++ { ++ m_accept_into = nullptr; ++ m_remote_endpoint = nullptr; ++ post(m_io_service, [&, h = std::exchange(m_accept_handler2, nullptr)] () mutable { ++ h(boost::system::error_code(error::operation_aborted) ++ , ip::tcp::socket(m_io_service)); ++ }); ++ } ++ } ++ ++ // if the user is not waiting for an incoming connection, there's no point ++ // in checking the queue ++ if (!m_accept_handler && !m_accept_handler2) return; ++ ++ if (m_incoming_conns.empty()) return; ++ ++ std::shared_ptr c = std::move(m_incoming_conns.front()); ++ m_incoming_conns.erase(m_incoming_conns.begin()); ++ ++ // this was initiated at least one 3-way handshake ago. ++ // we can pick it up and consider it connected ++ if (m_remote_endpoint) *m_remote_endpoint = c->ep[0]; ++ m_remote_endpoint = nullptr; ++ ++ boost::system::error_code ec; ++ // if the acceptor socket is closed. Any potential socket in the queue ++ m_accept_into->internal_connect(m_bound_to, c, ec); ++ ++ // notify the other end ++ aux::packet p; ++ p.from = asio::ip::udp::endpoint(m_bound_to.address(), m_bound_to.port()); ++ if (ec) ++ { ++ c->hops[1] = route(); ++ p.type = aux::packet::type_t::error; ++ p.ec = ec; ++ } ++ else ++ { ++ // TODO: extend pcap logging to include SYN+ACK packets ++ p.type = aux::packet::type_t::syn_ack; ++ } ++ p.channel = c; ++ p.overhead = 28; ++ p.hops = p.channel->hops[0]; ++ ++ forward_packet(std::move(p)); ++ ++ try ++ { ++ if (m_accept_handler) ++ { ++ post(m_io_service, bind_handler(std::exchange(m_accept_handler, nullptr), ec)); ++ } ++ else if (m_accept_handler2) ++ { ++ post(m_io_service, bind_handler(std::exchange(m_accept_handler2, nullptr) ++ , ec, std::move(*m_accept_into))); ++ } ++ } ++ catch (...) ++ { ++ m_new_socket.reset(); ++ m_accept_into = nullptr; ++ throw; ++ } ++ m_new_socket.reset(); ++ m_accept_into = nullptr; ++ } ++ ++ bool tcp::acceptor::internal_is_listening() ++ { ++ return m_queue_size_limit > 0; ++ } ++} ++} ++} ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/default_config.cpp +@@ -0,0 +1,106 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#include "simulator/simulator.hpp" ++#include "simulator/queue.hpp" ++#include ++ ++typedef sim::chrono::high_resolution_clock::time_point time_point; ++typedef sim::chrono::high_resolution_clock::duration duration; ++using sim::asio::ip::address_v4; ++using sim::asio::ip::address_v6; ++using sim::asio::ip::address; ++using sim::chrono::milliseconds; ++using sim::chrono::duration_cast; ++ ++namespace sim { ++ ++ void default_config::build(simulation& sim) ++ { ++ // 0 bandwidth and 0 queue means infinite. The network itself only adds ++ // 50 ms latency ++ m_network = std::make_shared(std::ref(sim.get_io_context()) ++ , 0, duration_cast(milliseconds(30)), 0, "network"); ++ m_sim = ∼ ++ } ++ ++ void default_config::clear() ++ { ++ m_network.reset(); ++ m_outgoing.clear(); ++ m_incoming.clear(); ++ } ++ ++ route default_config::channel_route( ++ asio::ip::address /* src */ ++ , asio::ip::address /* dst */) ++ { ++ return route().append(m_network); ++ } ++ ++ route default_config::incoming_route(asio::ip::address ip) ++ { ++ // incoming download rate is 800kB/s with a 200 kB queue ++ // and 1 ms forwarding delay ++ auto it = m_incoming.find(ip); ++ if (it != m_incoming.end()) return route().append(it->second); ++ it = m_incoming.insert(it, std::make_pair(ip, std::make_shared( ++ std::ref(m_sim->get_io_context()), 800 * 1000 ++ , duration_cast(milliseconds(1)), 200 * 1000, "DSL modem in"))); ++ return route().append(it->second); ++ } ++ ++ int default_config::path_mtu( ++ asio::ip::address /* ip1 */ ++ , asio::ip::address /* ip2 */) ++ { ++ return 1475; ++ } ++ ++ // return the hops an outgoing packet from ep need to traverse before ++ // reaching the network (for instance a DSL modem) ++ route default_config::outgoing_route(asio::ip::address ip) ++ { ++ // outgoing upload rate is 200kB/s with a 200 kB queue ++ // and 1 ms forwarding delay ++ auto it = m_outgoing.find(ip); ++ if (it != m_outgoing.end()) return route().append(it->second); ++ it = m_outgoing.insert(it, std::make_pair(ip, std::make_shared( ++ std::ref(m_sim->get_io_context()), 200 * 1000 ++ , duration_cast(milliseconds(1)), 200 * 1000, "DSL modem out"))); ++ return route().append(it->second); ++ } ++ ++ duration default_config::hostname_lookup( ++ asio::ip::address const& /* requestor */ ++ , std::string hostname ++ , std::vector& result ++ , boost::system::error_code& ec) ++ { ++ if (hostname == "localhost") ++ { ++ result = { asio::ip::make_address_v6("::1") ++ , asio::ip::make_address_v4("127.0.0.1") }; ++ return duration_cast(chrono::microseconds(1)); ++ } ++ ++ ec = boost::system::error_code(asio::error::host_not_found); ++ return duration_cast(chrono::milliseconds(100)); ++ } ++} ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/high_resolution_clock.cpp +@@ -0,0 +1,51 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#include "simulator/simulator.hpp" ++ ++#include ++ ++#include "simulator/push_warnings.hpp" ++#include ++#include "simulator/pop_warnings.hpp" ++ ++namespace sim { namespace chrono { ++ namespace { ++ ++ // this is the global simulation timer ++ high_resolution_clock::time_point g_simulation_time; ++ } ++ ++ high_resolution_clock::time_point high_resolution_clock::now() ++ { ++ return g_simulation_time; ++ } ++ ++ void high_resolution_clock::fast_forward(high_resolution_clock::duration d) ++ { ++ g_simulation_time += d; ++ } ++ ++ void reset_clock() ++ { ++ g_simulation_time = high_resolution_clock::time_point{}; ++ } ++ ++} // chrono ++} // sim ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/high_resolution_timer.cpp +@@ -0,0 +1,141 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#include "simulator/simulator.hpp" ++#include "simulator/handler_allocator.hpp" ++ ++#include ++#include ++ ++namespace sim ++{ ++ ++ namespace asio { ++ ++ high_resolution_timer::high_resolution_timer(io_context& ioc) ++ : m_expiration_time(time_type()) ++ , m_io_service(&ioc) ++ , m_expired(true) ++ { ++ } ++ ++ high_resolution_timer::high_resolution_timer(io_context& ioc, ++ const time_type& expiry_time) ++ : m_expiration_time(time_type()) ++ , m_io_service(&ioc) ++ , m_expired(true) ++ { ++ expires_at(expiry_time); ++ } ++ ++ high_resolution_timer::high_resolution_timer(io_context& ioc, ++ const duration_type& expiry_time) ++ : m_expiration_time(time_type()) ++ , m_io_service(&ioc) ++ , m_expired(true) ++ { ++ expires_after(expiry_time); ++ } ++ ++ high_resolution_timer::~high_resolution_timer() ++ { ++ cancel(); ++ } ++ ++ std::size_t high_resolution_timer::cancel() ++ { ++ if (m_expired) return 0; ++ m_expired = true; ++ m_io_service->remove_timer(this); ++ if (!m_handler) return 0; ++ fire(boost::asio::error::operation_aborted); ++ return 1; ++ } ++ ++ std::size_t high_resolution_timer::cancel_one() ++ { ++ // TODO: support multiple handlers ++ return cancel(); ++ } ++ ++ high_resolution_timer::time_type high_resolution_timer::expiry() const ++ { return m_expiration_time; } ++ ++ std::size_t high_resolution_timer::expires_at(high_resolution_timer::time_type const& expiry_time) ++ { ++ std::size_t ret = cancel(); ++ m_expiration_time = expiry_time; ++ m_expired = false; ++ m_io_service->add_timer(this); ++ return ret; ++ } ++ ++ std::size_t high_resolution_timer::expires_after(const duration_type& expiry_time) ++ { ++ std::size_t ret = cancel(); ++ m_expiration_time = chrono::high_resolution_clock::now() + expiry_time; ++ m_expired = false; ++ m_io_service->add_timer(this); ++ return ret; ++ } ++ ++ void high_resolution_timer::wait() ++ { ++ assert(false && "can't use synchcronous calls in simulator"); ++ boost::system::error_code ec; ++ wait(ec); ++ } ++ ++ void high_resolution_timer::wait(boost::system::error_code&) ++ { ++ assert(false && "can't use synchcronous calls in simulator"); ++ time_type now = chrono::high_resolution_clock::now(); ++ if (now >= m_expiration_time) return; ++ chrono::high_resolution_clock::fast_forward(m_expiration_time - now); ++ } ++ ++ void high_resolution_timer::async_wait(aux::function handler) ++ { ++ // TODO: support multiple handlers ++ assert(!m_handler); ++ m_handler = std::move(handler); ++ if (m_expired) ++ { ++ fire(boost::system::error_code()); ++ return; ++ } ++ } ++ ++ void high_resolution_timer::fire(boost::system::error_code ec) ++ { ++ m_expired = true; ++ if (!m_handler) return; ++ auto h = std::move(m_handler); ++ m_handler = nullptr; ++ post(*m_io_service, make_malloc(std::bind(std::move(h), ec))); ++ } ++ ++ high_resolution_timer::executor_type high_resolution_timer::get_executor() ++ { ++ return (*m_io_service).get_executor(); ++ } ++ ++ } // asio ++ ++} // sim ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/http_proxy.cpp +@@ -0,0 +1,362 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#include "simulator/simulator.hpp" ++#include "simulator/http_proxy.hpp" ++#include "simulator/http_server.hpp" // for helper functions ++ ++#include ++#include // for printf ++ ++using namespace sim::asio; ++using namespace sim::asio::ip; ++using namespace std::placeholders; ++ ++using boost::system::error_code; ++ ++namespace sim ++{ ++ using namespace aux; ++ ++ http_proxy::http_proxy(io_context& ios, unsigned short const listen_port) ++ : m_resolver(ios) ++ , m_listen_socket(ios) ++ , m_client_connection(ios) ++ , m_server_connection(ios) ++ , m_writing_to_server(false) ++ , m_num_client_in_bytes(0) ++ , m_num_server_out_bytes(0) ++ , m_num_in_bytes(0) ++ , m_close(false) ++ { ++ address local_ip = ios.get_ips().front(); ++ if (local_ip.is_v4()) ++ { ++ m_listen_socket.open(tcp::v4()); ++ m_listen_socket.bind(tcp::endpoint(address_v4::any(), listen_port)); ++ } ++ else ++ { ++ m_listen_socket.open(tcp::v6()); ++ m_listen_socket.bind(tcp::endpoint(address_v6::any(), listen_port)); ++ } ++ m_listen_socket.listen(); ++ ++ m_listen_socket.async_accept(m_client_connection, m_ep ++ , std::bind(&http_proxy::on_accept, this, _1)); ++ } ++ ++ void http_proxy::on_accept(error_code const& ec) ++ { ++ if (ec == asio::error::operation_aborted) ++ return; ++ ++ if (ec) ++ { ++ std::printf("http_proxy::on_accept: (%d) %s\n" ++ , ec.value(), ec.message().c_str()); ++ close_connection(); ++ return; ++ } ++ ++ std::printf("http_proxy accepted connection from: %s : %d\n", ++ m_ep.address().to_string().c_str(), m_ep.port()); ++ ++ // read http request ++ m_client_connection.async_read_some(asio::buffer( ++ &m_client_in_buffer[0], sizeof(m_client_in_buffer)) ++ , std::bind(&http_proxy::on_read_request, this, _1, _2)); ++ } ++ ++ void http_proxy::on_read_request(error_code const& ec, size_t bytes_transferred) try ++ { ++ if (ec) ++ { ++ std::printf("http_proxy::on_read_request: (%d) %s\n" ++ , ec.value(), ec.message().c_str()); ++ close_connection(); ++ return; ++ } ++ ++ m_num_client_in_bytes += int(bytes_transferred); ++ ++ // scan for end of HTTP request ++ int req_len = find_request_len(m_client_in_buffer, m_num_client_in_bytes); ++ while (req_len >= 0) ++ { ++ // parse request from [0, eor), connect to target server and forward ++ // the request. ++ http_request const req = parse_request(m_client_in_buffer, req_len); ++ forward_request(req); ++ ++ // pop this request from the receive buffer ++ memmove(m_client_in_buffer, m_client_in_buffer + req_len ++ , m_num_client_in_bytes - req_len); ++ m_num_client_in_bytes -= req_len; ++ ++ // is there another request in the buffer? ++ req_len = find_request_len(m_client_in_buffer, m_num_client_in_bytes); ++ } ++ ++ // read more from the client ++ m_client_connection.async_read_some(asio::buffer( ++ &m_client_in_buffer[m_num_client_in_bytes] ++ , sizeof(m_client_in_buffer) - m_num_client_in_bytes) ++ , std::bind(&http_proxy::on_read_request, this, _1, _2)); ++ } ++ catch (std::runtime_error& e) ++ { ++ std::printf("http_proxy::on_read_request() failed: %s\n" ++ , e.what()); ++ close_connection(); ++ } ++ ++ void http_proxy::forward_request(http_request const& req) ++ { ++ std::string out_request; ++ out_request = req.method; ++ out_request += ' '; ++ if (req.req.compare(0, 7, "http://") != 0) ++ { ++ std::printf("http_proxy::forward_request: expected full URL in request, got: %s\n" ++ , req.req.c_str()); ++ throw std::runtime_error("invalid request"); ++ } ++ ++ std::string::size_type const path_start = req.req.find_first_of('/', 7); ++ if (path_start == std::string::npos) out_request += '/'; ++ else out_request.append(req.req, path_start, std::string::npos); ++ out_request += " HTTP/1.1\r\n"; ++ ++ std::string::size_type const host_end = req.req.substr(0, path_start).find_last_of(':'); ++ ++ std::string host = req.req.substr(7, (host_end != std::string::npos && host_end > 7) ++ ? host_end - 7 : path_start - 7); ++ ++ // if the hostname is an IPv6 address, strip the brackets around it to ++ // make it parse correctly ++ if (host.size() >= 2 && host.front() == '[' && host.back() == ']') ++ host = host.substr(1, host.size() - 2); ++ ++ int const port = host_end == std::string::npos || host_end <= 7 ? 80 ++ : atoi(req.req.substr(host_end + 1, path_start).c_str()); ++ assert(port >= 0 && port < 0xffff); ++ ++ bool found_host = false; ++ for (auto const& h : req.headers) ++ { ++ if (h.first == "host") found_host = true; ++ ++ out_request += h.first; ++ out_request += ": "; ++ out_request += h.second; ++ out_request += "\r\n"; ++ } ++ if (!found_host) ++ { ++ out_request += "host: "; ++ out_request += host; ++ out_request += "\r\n"; ++ } ++ out_request += "\r\n"; ++ ++ if (m_num_server_out_bytes + out_request.size() > sizeof(m_server_out_buffer)) ++ { ++ std::printf("http_proxy: Too many queued server requests: %d bytes\n" ++ , int(m_num_server_out_bytes + out_request.size())); ++ throw std::runtime_error("pipeline too deep"); ++ } ++ memmove(&m_server_out_buffer[m_num_server_out_bytes] ++ , out_request.data(), out_request.size()); ++ m_num_server_out_bytes += int(out_request.size()); ++ ++ if (!m_server_connection.is_open()) ++ { ++ boost::system::error_code err; ++ tcp::endpoint target(make_address(host.c_str(), err) ++ , static_cast(port)); ++ if (err) ++ { ++ char port_str[10]; ++ std::snprintf(port_str, sizeof(port_str), "%d", port); ++ m_resolver.async_resolve(host, port_str ++ , std::bind(&http_proxy::on_domain_lookup, this, _1, _2)); ++ return; ++ } ++ open_forward_connection(target); ++ return; ++ } ++ ++ // TODO: make sure we're connecting/connected to the same (host, port) ++ // that this request is for. Don't support multiple servers ++ ++ write_server_send_buffer(); ++ } ++ ++ void http_proxy::on_domain_lookup(boost::system::error_code const& ec ++ , const asio::ip::tcp::resolver::results_type ips) ++ { ++ if (ec || ips.empty()) ++ { ++ if (ec) ++ { ++ std::printf("http_proxy::on_domain_lookup: (%d) %s\n" ++ , ec.value(), ec.message().c_str()); ++ } ++ else ++ { ++ std::printf("http_proxy::on_request_domain_lookup: empty response\n"); ++ } ++ error(503, "Resource Temporarily Unavailable"); ++ return; ++ } ++ open_forward_connection(ips.front().endpoint()); ++ } ++ ++ void http_proxy::open_forward_connection(const asio::ip::tcp::endpoint& target) ++ { ++ m_server_connection.open(target.protocol()); ++ ++ std::printf("http_proxy: async_connect: %s:%d\n" ++ , target.address().to_string().c_str(), target.port()); ++ m_server_connection.async_connect(target ++ , std::bind(&http_proxy::on_connected, this, _1)); ++ } ++ ++ void http_proxy::error(int code, char const* message) ++ { ++ std::string send_buffer = send_response(code, message); ++ memcpy(m_in_buffer, send_buffer.data(), send_buffer.size()); ++ asio::async_write(m_client_connection, asio::buffer( ++ &m_in_buffer[0], send_buffer.size()) ++ , std::bind(&http_proxy::close_connection, this)); ++ } ++ ++ void http_proxy::on_connected(boost::system::error_code const& ec) ++ { ++ if (ec) ++ { ++ std::printf("http_proxy::on_connected() connection failed: %s\n", ec.message().c_str()); ++ m_server_connection.close(); ++ error(503, "Service Temporarily Unavailable"); ++ return; ++ } ++ ++ std::printf("http_proxy: connected\n"); ++ ++ write_server_send_buffer(); ++ ++ m_server_connection.async_read_some( ++ asio::buffer(m_in_buffer, sizeof(m_in_buffer)) ++ , std::bind(&http_proxy::on_server_receive, this, _1, _2)); ++ } ++ ++ void http_proxy::write_server_send_buffer() ++ { ++ if (m_writing_to_server) return; ++ m_writing_to_server = true; ++ m_server_connection.async_write_some(asio::buffer( ++ &m_server_out_buffer[0], m_num_server_out_bytes) ++ , std::bind(&http_proxy::on_server_write, this, _1, _2)); ++ } ++ ++ void http_proxy::on_server_write(error_code const& ec, size_t bytes_transferred) ++ { ++ m_writing_to_server = false; ++ if (ec) ++ { ++ std::printf("http_proxy::on_server_write: (%d) %s\n" ++ , ec.value(), ec.message().c_str()); ++ close_connection(); ++ return; ++ } ++ ++ memmove(&m_server_out_buffer[0], &m_server_out_buffer[bytes_transferred] ++ , m_num_server_out_bytes - bytes_transferred); ++ m_num_server_out_bytes -= int(bytes_transferred); ++ ++ if (m_num_server_out_bytes > 0) ++ write_server_send_buffer(); ++ } ++ ++ // we received some data from the server, forward it to the server ++ void http_proxy::on_server_receive(boost::system::error_code const& ec ++ , std::size_t bytes_transferred) ++ { ++ if (ec) ++ { ++ std::printf("http_proxy: error reading from server: (%d) %s\n" ++ , ec.value(), ec.message().c_str()); ++ close_connection(); ++ return; ++ } ++ ++ asio::async_write(m_client_connection, asio::buffer(&m_in_buffer[0], bytes_transferred) ++ , std::bind(&http_proxy::on_server_forward, this, _1, _2)); ++ } ++ ++ void http_proxy::on_server_forward(error_code const& ec ++ , size_t) ++ { ++ if (ec) ++ { ++ std::printf("http_proxy: error writing to client: (%d) %s\n" ++ , ec.value(), ec.message().c_str()); ++ close_connection(); ++ return; ++ } ++ ++ m_server_connection.async_read_some( ++ sim::asio::buffer(m_in_buffer, sizeof(m_in_buffer)) ++ , std::bind(&http_proxy::on_server_receive, this, _1, _2)); ++ } ++ ++ void http_proxy::stop() ++ { ++ m_close = true; ++ m_listen_socket.close(); ++ } ++ ++ void http_proxy::close_connection() ++ { ++ m_num_client_in_bytes = 0; ++ m_num_server_out_bytes = 0; ++ m_num_in_bytes = 0; ++ ++ error_code err; ++ m_client_connection.close(err); ++ if (err) ++ { ++ std::printf("http_proxy::close: failed to close client connection (%d) %s\n" ++ , err.value(), err.message().c_str()); ++ } ++ m_server_connection.close(err); ++ if (err) ++ { ++ std::printf("http_proxy::close: failed to close server connection (%d) %s\n" ++ , err.value(), err.message().c_str()); ++ } ++ ++ if (m_close) return; ++ ++ // now we can accept another connection ++ m_listen_socket.async_accept(m_client_connection, m_ep ++ , std::bind(&http_proxy::on_accept, this, _1)); ++ } ++} ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/http_server.cpp +@@ -0,0 +1,425 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#include "simulator/simulator.hpp" ++#include "simulator/http_server.hpp" ++ ++#include ++#include // for printf ++ ++using namespace sim::asio; ++using namespace sim::asio::ip; ++using namespace std::placeholders; ++ ++using boost::system::error_code; ++ ++namespace sim ++{ ++ using namespace aux; ++ ++ namespace { ++ char const* find(char const* hay, int const hsize ++ , char const* needle, int const nsize) ++ { ++ for (int i = 0; i < hsize - nsize + 1; ++i) ++ { ++ if (memcmp(hay + i, needle, nsize) == 0) return hay + i; ++ } ++ return nullptr; ++ } ++ } ++ ++ std::string trim(std::string s) ++ { ++ if (s.empty()) return s; ++ ++ int start = 0; ++ int end = int(s.size()); ++ while (strchr(" \r\n\t", s[start]) != NULL && start < end) ++ { ++ ++start; ++ } ++ ++ while (strchr(" \r\n\t", s[end-1]) != NULL && end > start) ++ { ++ --end; ++ } ++ return s.substr(start, end - start); ++ } ++ ++ std::string lower_case(std::string s) ++ { ++ std::string ret; ++ std::transform(s.begin(), s.end(), std::back_inserter(ret) ++ , [](char c) { return static_cast(tolower(c)); } ); ++ return ret; ++ } ++ ++ std::string normalize(const std::string& s) ++ { ++ std::vector elements; ++ char const* start = s.c_str(); ++ if (*start == '/') ++start; ++ char const* slash = strchr(start, '/'); ++ while (slash != NULL) ++ { ++ std::string element(start, slash - start); ++ if (element != "..") ++ { ++ elements.push_back(element); ++ } else if (!elements.empty()) ++ { ++ elements.erase(elements.end()-1); ++ } ++ start = slash + 1; ++ slash = strchr(start, '/'); ++ } ++ elements.push_back(start); ++ ++ std::string ret; ++ for (auto const& e : elements) ++ { ++ ret += '/'; ++ ret += e; ++ } ++ ++ return ret; ++ } ++ ++ // TODO: extra_header should be a std::vector ++ std::string send_response(int code, char const* status_message ++ , int len, char const** extra_header) ++ { ++ std::string ret = "HTTP/1.1 " + std::to_string(code) + " " + status_message + "\r\n"; ++ ++ ret += "content-length: " + std::to_string(len) + "\r\n"; ++ ++ if (extra_header) ++ { ++ ret += extra_header[0]; ++ ret += extra_header[1]; ++ ret += extra_header[2]; ++ ret += extra_header[3]; ++ } ++ ret += "\r\n"; ++ ++ return ret; ++ } ++ ++ http_server::http_server(io_context& ios, unsigned short listen_port, int flags) ++ : m_ios(ios) ++ , m_listen_socket(ios) ++ , m_connection(ios) ++ , m_bytes_used(0) ++ , m_close(false) ++ , m_flags(flags) ++ { ++ address local_ip = ios.get_ips().front(); ++ if (local_ip.is_v4()) ++ { ++ m_listen_socket.open(tcp::v4()); ++ m_listen_socket.bind(tcp::endpoint(address_v4::any(), listen_port)); ++ } ++ else ++ { ++ m_listen_socket.open(tcp::v6()); ++ m_listen_socket.bind(tcp::endpoint(address_v6::any(), listen_port)); ++ } ++ m_listen_socket.listen(); ++ ++ m_listen_socket.async_accept(m_connection, m_ep ++ , std::bind(&http_server::on_accept, this, _1)); ++ } ++ ++ void http_server::on_accept(error_code const& ec) ++ { ++ if (ec) ++ { ++ std::printf("http_server::on_accept: (%d) %s\n" ++ , ec.value(), ec.message().c_str()); ++ close_connection(); ++ return; ++ } ++ ++ ++m_accepted_connections; ++ ++ std::printf("http_server accepted connection from: %s : %d\n", ++ m_ep.address().to_string().c_str(), m_ep.port()); ++ ++ read(); ++ } ++ ++ void http_server::register_handler(std::string const& path, handler_t h) ++ { ++ m_handlers[path] = std::move(h); ++ } ++ ++ void http_server::register_content(std::string const& path ++ , std::int64_t const size, generator_t gen) ++ { ++ m_handlers[path] = [gen,size](std::string, std::string, std::map& hdr) ++ { ++ std::int64_t start = 0; ++ std::int64_t end = size; ++ ++ auto it = hdr.find("range"); ++ bool const range_req = it != hdr.end(); ++ if (range_req) ++ { ++ std::string range = it->second; ++ // skip "bytes " ++ range = range.substr(range.find_first_of('=') + 1); ++ start = std::stoll(range.substr(0, range.find('-'))); ++ end = std::stoll(range.substr(range.find_first_of('-') + 1)) + 1; ++ } ++ ++ std::string header = "Content-Range: bytes " + std::to_string(start) ++ + "-" + std::to_string(end-1) + "/" + std::to_string(end-start) + "\r\n"; ++ char const* extra_headers[4] = { header.c_str(), "", "", ""}; ++ ++ return sim::send_response(range_req ? 206 : 200 ++ , range_req ? "Partial Content" : "OK" ++ , int(end - start), range_req ? extra_headers : nullptr) ++ + gen(start, end - start); ++ }; ++ } ++ ++ void http_server::register_redirect(std::string const& path, std::string const& target) ++ { ++ m_handlers[path] = [target](std::string, std::string, std::map&) ++ { ++ std::string header = "Location: " + target + "\r\n"; ++ char const* extra_headers[4] = { header.c_str(), "", "", ""}; ++ return sim::send_response(301, "Moved Permanently", 0, extra_headers); ++ }; ++ } ++ ++ void http_server::register_stall_handler(std::string const& path){ ++ m_stall_handlers.insert(path); ++ } ++ ++ void http_server::read() ++ { ++ if (m_bytes_used >= int(m_recv_buffer.size()) / 2) ++ { ++ m_recv_buffer.resize((std::max)(500, m_bytes_used * 2)); ++ } ++ assert(int(m_recv_buffer.size()) > m_bytes_used); ++ m_connection.async_read_some(asio::buffer(&m_recv_buffer[m_bytes_used] ++ , m_recv_buffer.size() - m_bytes_used) ++ , std::bind(&http_server::on_read, this, _1, _2)); ++ } ++ ++ http_request parse_request(char const* start, int len) ++ { ++ http_request ret; ++ ++ char const* const end_of_request = start + len; ++ char const* const space = find(start, len, " ", 1); ++ if (space == nullptr) ++ { ++ std::printf("http_server: failed to parse request:\n%s\n" ++ , std::string(start, len).c_str()); ++ throw std::runtime_error("parse failed"); ++ } ++ ++ char const* const space2 = find(space + 1 ++ , int(len - (space - start + 1)), " ", 1); ++ if (space2 == nullptr) ++ { ++ std::printf("http_server: failed to parse request:\n%s\n" ++ , std::string(start, len).c_str()); ++ throw std::runtime_error("parse failed"); ++ } ++ ret.method.assign(start, space); ++ ret.req.assign(space+1, space2); ++ if (ret.method != "CONNECT") { ++ ret.path.assign(normalize(ret.req.substr(0, ret.req.find_first_of('?')))); ++ } else { ++ ret.path.assign(ret.req); ++ } ++ std::printf("parse_request: %s %s [%s]\n" ++ , ret.method.c_str(), ret.path.c_str(), ret.req.c_str()); ++ ++ char const* header = find(space2, int(len - (space2 - start)), "\r\n", 2); ++ while (header != end_of_request - 4) ++ { ++ if (header == nullptr) ++ { ++ std::printf("http_server: failed to parse request:\n%s\n" ++ , std::string(start, len).c_str()); ++ throw std::runtime_error("parse failed"); ++ } ++ char const* const next = find(header + 2 ++ , int(len - (header + 2 - start)), "\r\n", 2); ++ char const* const value = static_cast(memchr(header, ':', len - (header - start))); ++ if (value == nullptr || next == nullptr || value > next) ++ { ++ std::printf("http_server: failed to parse request:\n%s\n" ++ , std::string(start, len).c_str()); ++ throw std::runtime_error("parse failed"); ++ } ++ ++ ret.headers[lower_case(trim(std::string(header, value)))] ++ = trim(std::string(value+1, next)); ++ ++ header = next; ++ } ++ return ret; ++ } ++ ++ int find_request_len(char const* buf, int const len) ++ { ++ char const* end_of_request = find(buf, len, "\r\n\r\n", 4); ++ if (end_of_request == nullptr) return -1; ++ return int(end_of_request - buf + 4); ++ } ++ ++ void http_server::on_read(error_code const& ec, size_t bytes_transferred) try ++ { ++ if (ec) ++ { ++ std::printf("http_server::on_read: (%d) %s\n" ++ , ec.value(), ec.message().c_str()); ++ close_connection(); ++ return; ++ } ++ ++ m_bytes_used += int(bytes_transferred); ++ ++ int const req_len = find_request_len(m_recv_buffer.data(), m_bytes_used); ++ if (req_len < 0) ++ { ++ read(); ++ return; ++ } ++ ++ http_request req = parse_request(m_recv_buffer.data(), req_len); ++ ++ m_recv_buffer.erase(m_recv_buffer.begin(), m_recv_buffer.begin() + req_len); ++ m_bytes_used -= req_len; ++ ++ auto it = m_handlers.find(req.path); ++ if (it == m_handlers.end()) ++ { ++ if (m_stall_handlers.find(req.path) != m_stall_handlers.end()) ++ { ++ return; ++ } ++ // no handler found, 404 ++ m_send_buffer = send_response(404, "Not Found"); ++ } ++ else ++ { ++ m_send_buffer = it->second(req.method, req.req, req.headers); ++ } ++ ++ // decide whether to close the connection after this response, and signal ++ // it to the client appropriately. ++ bool close; ++ if (m_flags & http_1_0) ++ { ++ // an HTTP/1.0 server closes after every response and does not use the ++ // Connection header (that is an HTTP/1.1 mechanism). Downgrade the ++ // status line so the client detects this from the protocol version. ++ close = true; ++ auto const ver = m_send_buffer.find("HTTP/1.1"); ++ if (ver != std::string::npos) ++ m_send_buffer.replace(ver, 8, "HTTP/1.0"); ++ } ++ else ++ { ++ // close if the client asked us to, or if this server is not ++ // configured for keep-alive. When we do, advertise it with a ++ // "Connection: close" response header so the client knows not to ++ // reuse the socket (rather than discovering it via a failed write). ++ close = lower_case(req.headers["connection"]) == "close" ++ || !(m_flags & keep_alive); ++ if (close) ++ { ++ auto const status_end = m_send_buffer.find("\r\n"); ++ if (status_end != std::string::npos) ++ { ++ assert(m_send_buffer.find("Connection:") == std::string::npos); ++ m_send_buffer.insert(status_end + 2, "Connection: close\r\n"); ++ } ++ } ++ } ++ ++ async_write(m_connection, asio::buffer(m_send_buffer.data() ++ , m_send_buffer.size()), std::bind(&http_server::on_write ++ , this, _1, _2, close)); ++ } ++ catch (std::exception& e) ++ { ++ std::printf("http_server::on_read() failed: %s\n" ++ , e.what()); ++ close_connection(); ++ } ++ ++ void http_server::on_write(error_code const& ec ++ , size_t /* bytes_transferred */ ++ , bool close) ++ { ++ if (ec) ++ { ++ std::printf("http_server::on_write: (%d) %s\n" ++ , ec.value(), ec.message().c_str()); ++ close_connection(); ++ return; ++ } ++ ++ if (!close) ++ { ++ // try to read another request out of the buffer ++ post(m_ios, std::bind(&http_server::on_read, this, error_code(), 0)); ++ } ++ else ++ { ++ close_connection(); ++ } ++ } ++ ++ void http_server::stop() ++ { ++ m_close = true; ++ m_listen_socket.close(); ++ } ++ ++ void http_server::close_connection() ++ { ++ m_recv_buffer.clear(); ++ m_bytes_used = 0; ++ ++ error_code err; ++ m_connection.close(err); ++ if (err) ++ { ++ std::printf("http_server::close: failed to close connection (%d) %s\n" ++ , err.value(), err.message().c_str()); ++ return; ++ } ++ ++ if (m_close) return; ++ ++ // now we can accept another connection ++ m_listen_socket.async_accept(m_connection, m_ep ++ , std::bind(&http_server::on_accept, this, _1)); ++ } ++} ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/io_service.cpp +@@ -0,0 +1,272 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#include "simulator/simulator.hpp" ++ ++#include ++#include ++ ++namespace sim { namespace asio { ++ ++ io_context::io_context(sim::simulation& sim) ++ : io_context(sim, std::vector()) ++ {} ++ ++ io_context::io_context(sim::simulation& sim, asio::ip::address const& ip) ++ : io_context(sim, std::vector{ip}) ++ {} ++ ++ io_context::io_context(sim::simulation& sim, std::vector const& ips) ++ : m_sim(sim) ++ , m_ips(ips) ++ , m_stopped(false) ++ { ++ for (auto const& ip : m_ips) ++ { ++ m_outgoing_route[ip] = m_sim.config().outgoing_route(ip); ++ m_incoming_route[ip] = m_sim.config().incoming_route(ip); ++ } ++ m_sim.add_io_service(this); ++ } ++ ++ io_context::~io_context() ++ { ++ m_sim.remove_io_service(this); ++ } ++ ++ // this constructor is never meant to be called (hence the assert). It only ++ // exists to satisfy interfaces that require a default-like constructor. The ++ // null reference is deliberate, so suppress the warning about it. ++#if defined __clang__ ++#pragma clang diagnostic push ++#pragma clang diagnostic ignored "-Wnull-dereference" ++#elif defined __GNUC__ ++#pragma GCC diagnostic push ++#pragma GCC diagnostic ignored "-Wnull-dereference" ++#endif ++ io_context::io_context(std::size_t) ++ : m_sim(*reinterpret_cast(0)) ++ { ++ assert(false); ++ } ++#if defined __clang__ ++#pragma clang diagnostic pop ++#elif defined __GNUC__ ++#pragma GCC diagnostic pop ++#endif ++ ++ int io_context::get_path_mtu(const asio::ip::address& source, const asio::ip::address& dest) const ++ { ++ // TODO: it would be nice to actually traverse the virtual network nodes ++ // and ask for their MTU instead ++ assert(std::count(m_ips.begin(), m_ips.end(), source) > 0 && "source address must be a local address to this node/io_context"); ++ return m_sim.config().path_mtu(source, dest); ++ } ++ ++ void io_context::stop() ++ { ++ // TODO: cancel all outstanding handler associated with this io_context ++ m_stopped = true; ++ } ++ ++ bool io_context::stopped() const ++ { ++ return m_stopped; ++ } ++ ++ void io_context::restart() ++ { ++ m_stopped = false; ++ } ++ ++ std::size_t io_context::run() ++ { ++ assert(false); ++ return 0; ++ } ++ ++ std::size_t io_context::run(boost::system::error_code&) ++ { ++ assert(false); ++ return 0; ++ } ++ ++ std::size_t io_context::poll() ++ { ++ assert(false); ++ return 0; ++ } ++ ++ std::size_t io_context::poll(boost::system::error_code&) ++ { ++ assert(false); ++ return 0; ++ } ++ ++ std::size_t io_context::poll_one() ++ { ++ assert(false); ++ return 0; ++ } ++ ++ std::size_t io_context::poll_one(boost::system::error_code&) ++ { ++ assert(false); ++ return 0; ++ } ++ ++ // private interface ++ ++ void io_context::add_timer(high_resolution_timer* t) ++ { ++ m_sim.add_timer(t); ++ } ++ ++ void io_context::remove_timer(high_resolution_timer* t) ++ { ++ m_sim.remove_timer(t); ++ } ++ ++ boost::asio::io_context& io_context::get_internal_service() ++ { return m_sim.get_internal_service(); } ++ ++ ip::tcp::endpoint io_context::bind_socket(ip::tcp::socket* socket ++ , ip::tcp::endpoint ep, boost::system::error_code& ec) ++ { ++ assert(!m_ips.empty() && "you cannot use an internal io_context (one without an IP address) for creating and binding sockets"); ++ if (ep.address() == ip::address_v4::any()) ++ { ++ auto it = std::find_if(m_ips.begin(), m_ips.end() ++ , [](ip::address const& ip) { return ip.is_v4(); } ); ++ if (it == m_ips.end()) ++ { ++ ec.assign(boost::system::errc::address_not_available ++ , boost::system::generic_category()); ++ return ip::tcp::endpoint(); ++ } ++ // TODO: pick the first local endpoint for now. In the future we may ++ // want have a bias toward ++ ep.address(*it); ++ } ++ else if (ep.address() == ip::address_v6::any()) ++ { ++ auto it = std::find_if(m_ips.begin(), m_ips.end() ++ , [](ip::address const& ip) { return ip.is_v6(); } ); ++ if (it == m_ips.end()) ++ { ++ ec.assign(boost::system::errc::address_not_available ++ , boost::system::generic_category()); ++ return ip::tcp::endpoint(); ++ } ++ // TODO: pick the first local endpoint for now. In the future we may ++ // want have a bias toward ++ ep.address(*it); ++ } ++ else if (std::count(m_ips.begin(), m_ips.end(), ep.address()) == 0) ++ { ++ // you can only bind to the IP assigned to this node. ++ // TODO: support loopback ++ ec.assign(boost::system::errc::address_not_available ++ , boost::system::generic_category()); ++ return ip::tcp::endpoint(); ++ } ++ ++ return m_sim.bind_socket(socket, ep, ec); ++ } ++ ++ void io_context::unbind_socket(ip::tcp::socket* socket ++ , const ip::tcp::endpoint& ep) ++ { ++ m_sim.unbind_socket(socket, ep); ++ } ++ ++ void io_context::rebind_socket(asio::ip::tcp::socket* prev, asio::ip::tcp::socket* s, asio::ip::tcp::endpoint ep) ++ { ++ m_sim.rebind_socket(prev, s, ep); ++ } ++ ++ ip::udp::endpoint io_context::bind_udp_socket(ip::udp::socket* socket ++ , ip::udp::endpoint ep, boost::system::error_code& ec) ++ { ++ assert(!m_ips.empty() && "you cannot use an internal io_context (one without an IP address) for creating and binding sockets"); ++ if (ep.address() == ip::address_v4::any()) ++ { ++ auto it = std::find_if(m_ips.begin(), m_ips.end() ++ , [](ip::address const& ip) { return ip.is_v4(); }); ++ if (it == m_ips.end()) ++ { ++ ec.assign(boost::system::errc::address_not_available ++ , boost::system::generic_category()); ++ return ip::udp::endpoint(); ++ } ++ // TODO: pick the first local endpoint for now. In the future we may ++ // want have a bias toward ++ ep.address(*it); ++ } ++ else if (ep.address() == ip::address_v6::any()) ++ { ++ auto it = std::find_if(m_ips.begin(), m_ips.end() ++ , [](ip::address const& ip) { return ip.is_v6(); }); ++ if (it == m_ips.end()) ++ { ++ ec.assign(boost::system::errc::address_not_available ++ , boost::system::generic_category()); ++ return ip::udp::endpoint(); ++ } ++ // TODO: pick the first local endpoint for now. In the future we may ++ // want have a bias toward ++ ep.address(*it); ++ } ++ else if (std::count(m_ips.begin(), m_ips.end(), ep.address()) == 0) ++ { ++ // you can only bind to the IP assigned to this node. ++ // TODO: support loopback ++ ec.assign(boost::system::errc::address_not_available ++ , boost::system::generic_category()); ++ return ip::udp::endpoint(); ++ } ++ ++ return m_sim.bind_udp_socket(socket, ep, ec); ++ } ++ ++ void io_context::unbind_udp_socket(ip::udp::socket* socket ++ , const ip::udp::endpoint& ep) ++ { ++ m_sim.unbind_udp_socket(socket, ep); ++ } ++ ++ void io_context::rebind_udp_socket(asio::ip::udp::socket* socket, asio::ip::udp::endpoint ep) ++ { ++ m_sim.rebind_udp_socket(socket, ep); ++ } ++ ++ std::shared_ptr io_context::internal_connect(ip::tcp::socket* s ++ , ip::tcp::endpoint const& target, boost::system::error_code& ec) ++ { ++ return m_sim.internal_connect(s, target, ec); ++ } ++ ++ route io_context::find_udp_socket(asio::ip::udp::socket const& socket ++ , ip::udp::endpoint const& ep) ++ { ++ return m_sim.find_udp_socket(socket, ep); ++ } ++ ++} // asio ++} // sim ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/nat.cpp +@@ -0,0 +1,47 @@ ++/* ++ ++Copyright (c) 2018, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#include "simulator/nat.hpp" ++#include "simulator/simulator.hpp" ++#include "simulator/packet.hpp" ++#include ++ ++namespace sim { ++ ++ nat::nat(asio::ip::address external_addr) : m_external_addr(external_addr) {} ++ ++ void nat::incoming_packet(aux::packet p) ++ { ++ // unconditionally replacing the "from" address is fine because of our ++ // simplified network model where the two paths of a connection are set up ++ // independently, and we can set up the nat hop only on the outgoing path ++ p.from.address(m_external_addr); ++ if (p.channel) { ++ p.channel->visible_ep[0].address(m_external_addr); ++ } ++ forward_packet(std::move(p)); ++ } ++ ++ // used for visualization ++ std::string nat::label() const ++ { ++ return std::string("NAT [") + m_external_addr.to_string() + "]"; ++ } ++ ++} // sim ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/pcap.cpp +@@ -0,0 +1,204 @@ ++/* ++ ++Copyright (c) 2017, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#include "simulator/pcap.hpp" ++#include "simulator/chrono.hpp" ++#include "simulator/packet.hpp" ++ ++using sim::chrono::duration_cast; ++using sim::chrono::seconds; ++ ++using sim::asio::ip::address_v4; ++using sim::asio::ip::tcp; ++using sim::asio::ip::udp; ++ ++namespace sim { namespace aux { ++ ++namespace { ++ ++ template ::value>::type> ++ void write(std::fstream& o, T const& value) ++ { o.write(reinterpret_cast(&value), sizeof(value)); } ++ ++ // documented here: ++ // http://www.tcpdump.org/linktypes.html ++ std::uint32_t const LINKTYPE_RAW = 101; ++ ++ struct ip_header ++ { ++ std::uint8_t version_len; ++ std::uint8_t dscp; ++ std::uint16_t length; ++ std::uint16_t identification; ++ std::uint16_t fragment; ++ std::uint8_t ttl; ++ std::uint8_t protocol; ++ std::uint16_t checksum; ++ std::uint32_t source_ip; ++ std::uint32_t destination_ip; ++ }; ++ ++ struct udp_header ++ { ++ std::uint16_t src_port; ++ std::uint16_t dst_port; ++ std::uint16_t length; ++ std::uint16_t checksum; ++ }; ++ ++ struct tcp_header ++ { ++ std::uint16_t src_port; ++ std::uint16_t dst_port; ++ std::uint32_t seq_nr; ++ std::uint32_t ack_nr; ++ std::uint8_t offset; ++ std::uint8_t flags; ++ std::uint16_t window_size; ++ std::uint16_t checksum; ++ std::uint16_t urgent; ++ }; ++} ++ ++ pcap::pcap(char const* filename) ++ : m_file(filename, std::ios_base::out | std::ios_base::trunc | std::ios_base::binary) ++ { ++ // file format documented here: ++ // https://wiki.wireshark.org/Development/LibpcapFileFormat ++ // write pcap file header ++ write(m_file, std::uint32_t(0xa1b2c3d4)); // magic number ++ write(m_file, std::uint16_t(2)); // versopm major ++ write(m_file, std::uint16_t(4)); // versopm minor ++ write(m_file, std::int32_t(0)); // thiszone ++ write(m_file, std::uint32_t(0)); // sigfigs ++ write(m_file, std::uint32_t(0xffff)); // snaplen ++ write(m_file, LINKTYPE_RAW); // network ++ } ++ ++ void write_ip_header(std::fstream& file, int const size, int const protocol ++ , address_v4 const source, address_v4 const dest) ++ { ++ ip_header const header = { ++ (4 << 4) | 5, // version_len ++ 0, // dscp ++ htons(std::uint16_t(size)), // length ++ 0, // identification ++ 0, // fragment ++ 200, // ttl ++ std::uint8_t(protocol), // protocol ++ 0, // checksum ++ htonl(std::uint32_t(source.to_uint())), // source ip ++ htonl(std::uint32_t(dest.to_uint())) // destination ip ++ }; ++ ++ write(file, header); ++ } ++ ++ void write_udp_header(std::fstream& file, int const size, int const src_port ++ , int const dst_port) ++ { ++ assert(src_port != 0); ++ assert(dst_port != 0); ++ udp_header const header = { ++ htons(std::uint16_t(src_port)), // source port ++ htons(std::uint16_t(dst_port)), // destination port ++ htons(std::uint16_t(sizeof(udp_header) + size)), // length ++ 0 // checksum ++ }; ++ ++ write(file, header); ++ } ++ ++ void write_tcp_header(std::fstream& file, int const src_port ++ , int const dst_port, std::uint32_t const seq_nr) ++ { ++ assert(src_port != 0); ++ assert(dst_port != 0); ++ tcp_header const header = { ++ htons(std::uint16_t(src_port)), // source port ++ htons(std::uint16_t(dst_port)), // destination port ++ htonl(seq_nr), // sequence number ++ std::uint32_t(0), // acknowledgement number ++ std::uint8_t(5 << 4), // header size (data offset) ++ std::uint8_t(0), // flags ++ htons(std::uint16_t(65535)), // window size ++ std::uint16_t(0), // checksum ++ std::uint16_t(0) // urgent offset ++ }; ++ ++ write(file, header); ++ } ++ ++ void pcap::log_tcp(packet const& p, tcp::endpoint const src ++ , tcp::endpoint const dst) ++ { ++ // synthesize IP/TCP header and write packet ++ auto const now = chrono::high_resolution_clock::now(); ++ ++ // just an arbitrary posix time used as starting point ++ std::uint32_t const sim_start_time = 441794304; ++ std::uint32_t const secs = static_cast(duration_cast(now.time_since_epoch()).count()); ++ std::uint32_t const usecs = static_cast(duration_cast(now.time_since_epoch() - seconds(secs)).count()); ++ ++ std::uint32_t const packet_size = static_cast(sizeof(ip_header) + sizeof(tcp_header) + p.buffer.size()); ++ ++ write(m_file, sim_start_time + secs); ++ write(m_file, usecs); ++ write(m_file, packet_size); ++ write(m_file, packet_size); ++ ++ // 6 is the protocol number for TCP ++ write_ip_header(m_file, packet_size, 6 ++ , src.address().to_v4(), dst.address().to_v4()); ++ ++ // TODO: if the packet has error set with asio::error::eof ++ // set the FIN flag ++ write_tcp_header(m_file, p.from.port(), dst.port(), p.byte_counter); ++ ++ m_file.write(reinterpret_cast(p.buffer.data()), p.buffer.size()); ++ } ++ ++ void pcap::log_udp(packet const& p, udp::endpoint const src ++ , udp::endpoint const dst) ++ { ++ // synthesize IP/UDP header and write packet ++ auto const now = chrono::high_resolution_clock::now(); ++ ++ // just an arbitrary posix time used as starting point ++ std::uint32_t const sim_start_time = 441794304; ++ std::uint32_t const secs = static_cast(duration_cast(now.time_since_epoch()).count()); ++ std::uint32_t const usecs = static_cast(duration_cast(now.time_since_epoch() - seconds(secs)).count()); ++ ++ std::uint32_t const packet_size = static_cast(sizeof(ip_header) + sizeof(udp_header) + p.buffer.size()); ++ ++ write(m_file, sim_start_time + secs); ++ write(m_file, usecs); ++ write(m_file, packet_size); ++ write(m_file, packet_size); ++ ++ // 17 is the protocol number for UDP ++ write_ip_header(m_file, packet_size, 17 ++ , src.address().to_v4(), dst.address().to_v4()); ++ ++ write_udp_header(m_file, static_cast(p.buffer.size()), p.from.port(), static_cast(dst.port())); ++ ++ m_file.write(reinterpret_cast(p.buffer.data()), p.buffer.size()); ++ } ++ ++}} ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/queue.cpp +@@ -0,0 +1,142 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#include "simulator/queue.hpp" ++#include "simulator/handler_allocator.hpp" ++#include ++#include // for printf ++ ++typedef sim::chrono::high_resolution_clock::time_point time_point; ++typedef sim::chrono::high_resolution_clock::duration duration; ++ ++namespace sim ++{ ++ using namespace aux; ++ ++ queue::queue(asio::io_context& ios ++ , int bandwidth ++ , chrono::high_resolution_clock::duration propagation_delay ++ , int max_queue_size ++ , std::string name) ++ : m_max_queue_size(max_queue_size) ++ , m_forwarding_latency(propagation_delay) ++ , m_bandwidth(bandwidth) ++ , m_queue_size(0) ++ , m_node_name(name) ++ , m_forward_timer(ios) ++ , m_last_forward(chrono::high_resolution_clock::now()) ++ {} ++ ++ std::string queue::label() const ++ { ++ char ret[400]; ++ int p = std::snprintf(ret, sizeof(ret), "%s\n", m_node_name.c_str()); ++ ++ if (m_bandwidth != 0) ++ { ++ p += std::snprintf(ret + p, sizeof(ret) - p, "rate: %d kB/s\n" ++ , m_bandwidth / 1000); ++ } ++ ++ if (m_queue_size != 0) ++ { ++ p += std::snprintf(ret + p, sizeof(ret) - p, "queue: %d kB\n" ++ , m_queue_size / 1000); ++ } ++ ++ if (m_forwarding_latency.count() != 0) ++ { ++ p += std::snprintf(ret + p, sizeof(ret) - p, "latency: %d ms\n" ++ , int(chrono::duration_cast(m_forwarding_latency).count())); ++ } ++ ++ return ret; ++ } ++ ++ void queue::incoming_packet(aux::packet p) ++ { ++ const int packet_size = int(p.buffer.size() + p.overhead); ++ ++ // tail-drop ++ if (p.ok_to_drop() ++ && m_max_queue_size > 0 ++ && m_queue_size + packet_size > m_max_queue_size) ++ { ++ // if any hop on the network drops a packet, it has to return it to the ++ // sender. ++ auto drop_fun = std::move(p.drop_fun); ++ if (drop_fun) drop_fun(std::move(p)); ++ return; ++ } ++ ++ time_point const now = chrono::high_resolution_clock::now(); ++ ++ m_queue.emplace_back(now + m_forwarding_latency, std::move(p)); ++ m_queue_size += packet_size; ++ if (m_queue.size() > 1) return; ++ ++ begin_send_next_packet(); ++ } ++ ++ void queue::begin_send_next_packet() ++ { ++ time_point now = chrono::high_resolution_clock::now(); ++ ++ if (m_queue.front().ts > now) ++ { ++ m_forward_timer.expires_at(m_queue.front().ts); ++ m_forward_timer.async_wait(make_malloc(std::bind(&queue::begin_send_next_packet ++ , this))); ++ return; ++ } ++ ++ m_last_forward = now; ++ if (m_bandwidth == 0) ++ { ++ post(m_forward_timer.get_executor(), make_malloc(std::bind(&queue::next_packet_sent ++ , this))); ++ return; ++ } ++ const double nanoseconds_per_byte = 1000000000.0 ++ / double(m_bandwidth); ++ ++ aux::packet const& p = m_queue.front().pkt; ++ const int packet_size = int(p.buffer.size() + p.overhead); ++ ++ m_last_forward += chrono::duration_cast(chrono::nanoseconds( ++ boost::int64_t(nanoseconds_per_byte * packet_size))); ++ ++ m_forward_timer.expires_at(m_last_forward); ++ m_forward_timer.async_wait(make_malloc(std::bind(&queue::next_packet_sent ++ , this))); ++ } ++ ++ void queue::next_packet_sent() ++ { ++ aux::packet p = std::move(m_queue.front().pkt); ++ m_queue.erase(m_queue.begin()); ++ const int packet_size = int(p.buffer.size() + p.overhead); ++ m_queue_size -= packet_size; ++ ++ forward_packet(std::move(p)); ++ ++ if (m_queue.size()) ++ begin_send_next_packet(); ++ } ++} ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/resolver.cpp +@@ -0,0 +1,143 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#include "simulator/simulator.hpp" ++#include "simulator/handler_allocator.hpp" ++#include ++ ++typedef sim::chrono::high_resolution_clock::time_point time_point; ++typedef sim::chrono::high_resolution_clock::duration duration; ++ ++using namespace std::placeholders; ++ ++namespace sim { ++namespace asio { ++namespace ip { ++ ++ template ++ basic_resolver::basic_resolver(io_context& ios) ++ : m_ios(&ios) ++ , m_timer(ios) ++ {} ++ ++ template ++ basic_resolver::basic_resolver(basic_resolver&&) noexcept = default; ++ ++ template ++ basic_resolver& basic_resolver::operator=(basic_resolver&&) noexcept = default; ++ ++ template ++ void basic_resolver::async_resolve(std::string hostname, char const* service ++ , aux::function handler) ++ { ++ std::vector result; ++ boost::system::error_code ec; ++ ++ const chrono::high_resolution_clock::time_point start_time = ++ m_queue.empty() ? chrono::high_resolution_clock::now() : ++ m_queue.front().completion_time; ++ ++ assert(!m_ios->get_ips().empty() && "internal io service objects can only " ++ "be used for timers"); ++ ++ // if the hostname is an IP address, resolve it immediately ++ asio::ip::address addr = make_address_v4(hostname, ec); ++ if (ec) addr = make_address_v6(hostname, ec); ++ if (!ec) ++ { ++ const chrono::high_resolution_clock::time_point t = chrono::high_resolution_clock::now() ++ + chrono::microseconds(1); ++ results_type ips; ++ int const port = atoi(service); ++ assert(port >= 0 && port <= 0xffff); ++ ips.emplace_back( ++ typename Protocol::endpoint(addr, static_cast(port)) ++ , hostname, service); ++ result_t res{t, ec, std::move(ips), std::move(handler) }; ++ m_queue.insert(m_queue.begin(), std::move(res)); ++ m_timer.expires_at(m_queue.front().completion_time); ++ m_timer.async_wait(aux::make_malloc(std::bind(&basic_resolver::on_lookup, this, _1))); ++ return; ++ } ++ ec.clear(); ++ ++ const chrono::high_resolution_clock::time_point completion_time = ++ start_time ++ + m_ios->sim().config().hostname_lookup(m_ios->get_ips().front(), hostname ++ , result, ec); ++ ++ results_type ips; ++ ++ int const port = atoi(service); ++ assert(port >= 0 && port <= 0xffff); ++ ++ for (auto const& ip : result) ++ { ++ ips.emplace_back( ++ typename Protocol::endpoint(ip, static_cast(port)) ++ , hostname, service); ++ } ++ ++ result_t res{ completion_time, ec, std::move(ips), std::move(handler)}; ++ m_queue.emplace_back(std::move(res)); ++ ++ m_timer.expires_at(m_queue.front().completion_time); ++ m_timer.async_wait(aux::make_malloc(std::bind(&basic_resolver::on_lookup, this, _1))); ++ } ++ ++ template ++ void basic_resolver::on_lookup(boost::system::error_code const& ec) ++ { ++ if (ec == asio::error::operation_aborted) return; ++ ++ if (m_queue.empty()) return; ++ ++ typename queue_t::value_type v = std::move(m_queue.front()); ++ m_queue.erase(m_queue.begin()); ++ ++ // once the handler is called, it's possible the last reference keeping ++ // this object (basic_resolver) alive is released and we're deleted. Make ++ // sure to not touch any members after the handler in that case. ++ bool const empty = m_queue.empty(); ++ v.handler(v.err, std::move(v.ips)); ++ if (empty) return; ++ ++ m_timer.expires_at(m_queue.front().completion_time); ++ m_timer.async_wait(aux::make_malloc(std::bind(&basic_resolver::on_lookup, this, _1))); ++ } ++ ++ template ++ void basic_resolver::cancel() ++ { ++ queue_t q; ++ m_queue.swap(q); ++ for (auto& r : q) ++ { ++ r.err = asio::error::operation_aborted; ++ post(m_timer.get_executor(), aux::make_malloc(std::bind(std::move(r.handler) ++ , r.err, std::move(r.ips)))); ++ } ++ } ++ ++ // explicitly instantiate the functions ++ template struct basic_resolver; ++ template struct basic_resolver; ++} ++} ++} ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/simulation.cpp +@@ -0,0 +1,381 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#include "simulator/simulator.hpp" ++#include "simulator/packet.hpp" ++#include "simulator/pcap.hpp" ++ ++#include // for tie ++#include // for make_shared ++#include // for printf ++#include ++#include // for std::runtime_error ++ ++using namespace sim::asio; ++ ++namespace sim ++{ ++ simulation::simulation(configuration& config) ++ : m_config(config) ++ , m_internal_ios(new asio::io_context(*this)) ++ , m_service(1) ++ { ++ sim::chrono::reset_clock(); ++ m_config.build(*this); ++ } ++ ++ simulation::~simulation() ++ { ++ m_config.clear(); ++ ++ assert(m_timer_queue.empty()); ++ } ++ ++ std::size_t simulation::run() try ++ { ++ std::size_t ret = 0; ++ std::size_t last_executed = 0; ++ ++ // Detect a runaway loop where events keep firing but simulated time ++ // never advances - typically a timer being repeatedly scheduled at the ++ // current time (a 100% CPU spin that would otherwise hang run() ++ // forever). Legitimate bursts of same-timestamp events are finite, so ++ // a large threshold reliably distinguishes them from an unbounded spin. ++ chrono::high_resolution_clock::time_point prev_time ++ = chrono::high_resolution_clock::now(); ++ std::int64_t stalled_iterations = 0; ++ constexpr std::int64_t max_stalled_iterations = 1000; ++ ++ do { ++ ++ m_service.restart(); ++ last_executed = m_service.poll(); ++ ret += last_executed; ++ ++ chrono::high_resolution_clock::time_point now ++ = chrono::high_resolution_clock::now(); ++ ++ std::lock_guard l(m_timer_queue_mutex); ++ if (!m_timer_queue.empty()) { ++ asio::high_resolution_timer* next_timer = *m_timer_queue.begin(); ++ chrono::high_resolution_clock::fast_forward(next_timer->expiry() - now); ++ ++ now = chrono::high_resolution_clock::now(); ++ ++ while (!m_timer_queue.empty() ++ && (*m_timer_queue.begin())->expiry() <= now) { ++ ++ next_timer = *m_timer_queue.begin(); ++ m_timer_queue.erase(m_timer_queue.begin()); ++ next_timer->fire(boost::system::error_code()); ++ ++last_executed; ++ ++ret; ++ } ++ } ++ ++ if (last_executed > 0) { ++ if (now == prev_time) { ++ if (++stalled_iterations > max_stalled_iterations) { ++ throw std::runtime_error("libsimulator: simulated time " ++ "has not advanced for too many consecutive iterations " ++ "while events keep firing. The simulation is stuck in a " ++ "busy loop, typically a timer repeatedly scheduled at " ++ "the current time (a 100% CPU spin)."); ++ } ++ } else { ++ stalled_iterations = 0; ++ prev_time = now; ++ } ++ } ++ ++// std::fprintf(stderr, "run: last_executed: %d stopped: %d timer-queue: %d\n" ++// , int(last_executed), m_stopped, int(m_timer_queue.size())); ++ } while (last_executed > 0 && !m_stopped); ++ ++// std::fprintf(stderr, "exiting simulation::run(): last_executed: %d stopped: %d timer-queue: %d ret: %d\n" ++// , int(last_executed), m_stopped, int(m_timer_queue.size()), int(ret)); ++ return ret; ++ } ++ catch (...) ++ { ++ // cancel all outstanding timers ++ // we make a copy, since cancelling the timers will mutate m_timer_queue ++ auto queue = m_timer_queue; ++ for (auto& t : queue) ++ t->cancel(); ++ ++ auto listen_sockets = m_listen_sockets; ++ for (auto& s : listen_sockets) ++ s.second->cancel(); ++ ++ auto udp_sockets = m_udp_sockets; ++ for (auto& s : udp_sockets) ++ s.second->cancel(); ++ m_stopped = true; ++ throw; ++ } ++ ++ void simulation::stop() { m_stopped = true; } ++ bool simulation::stopped() const { return m_stopped; } ++ void simulation::restart() { m_stopped = false; } ++ ++ void simulation::add_timer(asio::high_resolution_timer* t) ++ { ++ assert(!m_stopped); ++ if (t->expiry() == sim::chrono::high_resolution_clock::now()) ++ { ++ std::fprintf(stderr, "WARNING: timer scheduled for current time!\n"); ++ } ++ std::lock_guard l(m_timer_queue_mutex); ++ // make sure we get a deterministic ordering of timers with the same ++ // expiration time ++ auto it = std::upper_bound(m_timer_queue.begin(), m_timer_queue.end(), t, timer_compare()); ++ m_timer_queue.insert(it, t); ++ } ++ ++ void simulation::remove_timer(asio::high_resolution_timer* t) ++ { ++ std::lock_guard l(m_timer_queue_mutex); ++ if (m_timer_queue.empty()) return; ++ timer_queue_t::iterator begin; ++ timer_queue_t::iterator end; ++ std::tie(begin, end) = std::equal_range(m_timer_queue.begin(), m_timer_queue.end(), t, timer_compare()); ++ if (begin == end) return; ++ begin = std::find(begin, end, t); ++ if (begin == end) return; ++ m_timer_queue.erase(begin); ++ } ++ ++ void simulation::rebind_socket(ip::tcp::socket* prev, ip::tcp::socket* s, ip::tcp::endpoint ep) ++ { ++ auto i = m_listen_sockets.find(ep); ++ assert(i != m_listen_sockets.end()); ++ if (i->second != prev) return; ++ i->second = s; ++ } ++ ++ ip::tcp::endpoint simulation::bind_socket(ip::tcp::socket* socket ++ , ip::tcp::endpoint ep, boost::system::error_code& ec) ++ { ++ assert(ep.address() != boost::asio::ip::address()); ++ ++ if (ep.port() < 1024 && ep.port() > 0) ++ { ++ // emulate process not running as root ++ ec = boost::asio::error::access_denied; ++ return ip::tcp::endpoint(); ++ } ++ ++ if (ep.port() == 0) ++ { ++ // if the socket is being bound to port 0, it means the system picks a ++ // free port. We want to avoid re-using ports, because that may confuse ++ // wireshark when threading together the TCP streams. ++ ep.port(m_next_bind_port++); ++ if (m_next_bind_port > 65534) m_next_bind_port = 2000; ++ ++ listen_socket_iter_t i = m_listen_sockets.lower_bound(ep); ++ while (i != m_listen_sockets.end() && i->first == ep) ++ { ++ ep.port(ep.port() + 1); ++ if (ep.port() > 65530) ++ { ++ ec = boost::asio::error::address_in_use; ++ return ip::tcp::endpoint(); ++ } ++ i = m_listen_sockets.lower_bound(ep); ++ } ++ } ++ ++ listen_socket_iter_t i = m_listen_sockets.lower_bound(ep); ++ if (i != m_listen_sockets.end() && i->first == ep) ++ { ++ ec = boost::asio::error::address_in_use; ++ return ip::tcp::endpoint(); ++ } ++ ++ m_listen_sockets.insert(i, std::make_pair(ep, socket)); ++ ec.clear(); ++ return ep; ++ } ++ ++ void simulation::unbind_socket(ip::tcp::socket* socket ++ , const ip::tcp::endpoint& ep) ++ { ++ listen_socket_iter_t i = m_listen_sockets.find(ep); ++ if (i == m_listen_sockets.end() || i->second != socket) return; ++ m_listen_sockets.erase(i); ++ } ++ ++ void simulation::rebind_udp_socket(ip::udp::socket* socket, ip::udp::endpoint ep) ++ { ++ auto i = m_udp_sockets.find(ep); ++ assert(i != m_udp_sockets.end()); ++ i->second = socket; ++ } ++ ++ ip::udp::endpoint simulation::bind_udp_socket(ip::udp::socket* socket ++ , ip::udp::endpoint ep, boost::system::error_code& ec) ++ { ++ assert(ep.address() != boost::asio::ip::address()); ++ ++ if (ep.port() < 1024 && ep.port() > 0) ++ { ++ // emulate process not running as root ++ ec = boost::asio::error::access_denied; ++ return ip::udp::endpoint(); ++ } ++ ++ if (ep.port() == 0) ++ { ++ // if the socket is being bound to port 0, it means the system picks a ++ // free port. ++ ++ ep.port(m_next_bind_port++); ++ if (m_next_bind_port > 65534) m_next_bind_port = 2000; ++ udp_socket_iter_t i = m_udp_sockets.lower_bound(ep); ++ while (i != m_udp_sockets.end() && i->first == ep) ++ { ++ ep.port(ep.port() + 1); ++ if (ep.port() > 65530) ++ { ++ ec = boost::asio::error::address_in_use; ++ return ip::udp::endpoint(); ++ } ++ i = m_udp_sockets.lower_bound(ep); ++ } ++ } ++ ++ udp_socket_iter_t i = m_udp_sockets.lower_bound(ep); ++ if (i != m_udp_sockets.end() && i->first == ep) ++ { ++ ec = boost::asio::error::address_in_use; ++ return ip::udp::endpoint(); ++ } ++ ++ m_udp_sockets.insert(i, std::make_pair(ep, socket)); ++ ec.clear(); ++ return ep; ++ } ++ ++ void simulation::unbind_udp_socket(ip::udp::socket* socket ++ , const ip::udp::endpoint& ep) ++ { ++ udp_socket_iter_t i = m_udp_sockets.find(ep); ++ if (i == m_udp_sockets.end() || i->second != socket) return; ++ m_udp_sockets.erase(i); ++ } ++ ++ std::shared_ptr simulation::internal_connect( ++ asio::ip::tcp::socket* s ++ , ip::tcp::endpoint const& target, boost::system::error_code& ec) ++ { ++ // find remote socket ++ listen_sockets_t::iterator i = m_listen_sockets.find(target); ++ if (i == m_listen_sockets.end()) ++ { ++ ec = boost::system::error_code(error::connection_refused); ++ return std::shared_ptr(); ++ } ++ ++ // make sure it's a listening socket ++ ip::tcp::socket* remote = i->second; ++ if (!remote->internal_is_listening()) ++ { ++ ec = boost::system::error_code(error::connection_refused); ++ return std::shared_ptr(); ++ } ++ ++ // create a channel ++ std::shared_ptr c = std::make_shared(); ++ ++ asio::ip::tcp::endpoint from = s->local_bound_to(ec); ++ ++ route network_route = m_config.channel_route(from.address() ++ , target.address()); ++ c->hops[0] = remote->get_outgoing_route() + network_route + s->get_incoming_route(); ++ c->hops[1] = s->get_outgoing_route() + network_route + remote->get_incoming_route(); ++ ++ c->ep[0] = s->local_bound_to(ec); ++ c->ep[1] = remote->local_bound_to(ec); ++ ++ c->visible_ep[0] = s->local_bound_to(ec); ++ c->visible_ep[1] = remote->local_bound_to(ec); ++ ++ aux::packet p; ++ p.type = aux::packet::type_t::syn; ++ p.overhead = 28; ++ p.from = asio::ip::udp::endpoint(from.address(), from.port()); ++ p.channel = c; ++ if (ec) return std::shared_ptr(); ++ ++ p.hops = c->hops[1]; ++ ++ forward_packet(std::move(p)); ++ ++ return c; ++ } ++ ++ route simulation::find_udp_socket(asio::ip::udp::socket const& socket ++ , ip::udp::endpoint const& ep) ++ { ++ udp_socket_iter_t i = m_udp_sockets.find(ep); ++ if (i == m_udp_sockets.end()) ++ return route(); ++ ++ ip::udp::endpoint src = socket.local_bound_to(); ++ route network_route = m_config.channel_route(src.address(), ep.address()); ++ ++ // ask the socket for its incoming route ++ network_route.append(i->second->get_incoming_route()); ++ ++ return network_route; ++ } ++ ++ void simulation::add_io_service(asio::io_context* ios) ++ { ++ bool added = m_nodes.insert(ios).second; ++ (void)added; ++ assert(added); ++ } ++ ++ void simulation::remove_io_service(asio::io_context* ios) ++ { ++ auto it = m_nodes.find(ios); ++ assert(it != m_nodes.end()); ++ m_nodes.erase(it); ++ } ++ ++ std::vector simulation::get_all_io_services() const ++ { ++ std::vector ret; ++ ret.reserve(m_nodes.size()); ++ std::remove_copy_if( ++ m_nodes.begin(), m_nodes.end(), std::back_inserter(ret) ++ , [](io_context* ios) { return ios->get_ips().empty(); }); ++ return ret; ++ } ++ ++ void simulation::log_pcap(char const* filename) ++ { ++ std::printf("saving packet capture to: \"%s\"\n", filename); ++ m_pcap = std::unique_ptr(new aux::pcap(filename)); ++ } ++ ++} ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/simulator.cpp +@@ -0,0 +1,262 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#include "simulator/simulator.hpp" ++#include "simulator/packet.hpp" ++ ++#include ++#include ++#include ++#include // for printf ++ ++#include "simulator/push_warnings.hpp" ++#include ++#include "simulator/pop_warnings.hpp" ++ ++typedef sim::chrono::high_resolution_clock::time_point time_point; ++typedef sim::chrono::high_resolution_clock::duration duration; ++ ++namespace sim { ++namespace asio { ++namespace ip { ++ ++ default_config default_cfg; ++ ++} // ip ++} // asio ++ ++void forward_packet(aux::packet p) ++{ ++ std::shared_ptr next_hop = p.hops.pop_front(); ++ if (!next_hop) ++ { ++ std::fprintf(stderr, "packet lost\n"); ++ return; ++ } ++ next_hop->incoming_packet(std::move(p)); ++} ++ ++namespace ++{ ++ // this is a dummy sink for endpoints, wrapping an io_context ++ struct endpoint : sink ++ { ++ endpoint(asio::io_context& ioc) ++ : m_ioc(ioc) ++ {} ++ ++ virtual void incoming_packet(aux::packet /* p */) override final { assert(false); } ++ ++ virtual std::string label() const override final ++ { ++ std::string ret; ++ for (auto const& ip : m_ioc.get_ips()) ++ { ++ ret += ip.to_string(); ++ ret += " "; ++ } ++ return ret; ++ } ++ ++ virtual std::string attributes() const override final ++ { ++ return "shape=ellipse"; ++ } ++ ++ private: ++ asio::io_context& m_ioc; ++ }; ++} ++ ++namespace ++{ ++ std::string escape_label(std::string n) ++ { ++ std::string ret; ++ for (auto c : n) ++ { ++ if (c == '\n') ++ { ++ ret += "\\n"; ++ continue; ++ } ++ if (c == '\"') ++ { ++ ret += "\\\""; ++ continue; ++ } ++ ret += c; ++ } ++ return ret; ++ } ++} ++ ++void dump_network_graph(simulation const& s, const std::string& filename) ++{ ++ // all edges (directed). ++ std::set, std::shared_ptr>> edges; ++ ++ // all network nodes ++ std::unordered_set> nodes; ++ ++ // local nodes (subgrapgs) ++ std::vector>> local_nodes; ++ ++ const std::vector io_services = s.get_all_io_services(); ++ ++ for (auto ioc : io_services) ++ { ++ std::shared_ptr ep = std::make_shared(*ioc); ++ local_nodes.push_back(std::unordered_set>()); ++ local_nodes.back().insert(ep); ++ ++ for (auto const& ip : ioc->get_ips()) ++ { ++ route in = ioc->get_incoming_route(ip); ++ route out = ioc->get_outgoing_route(ip); ++ ++ // this is the outgoing node for this endpoint. This is ++ // how it connects to the network. ++ const std::shared_ptr egress = out.empty() ? ep : out.last(); ++ ++ // first add both the incoming and outgoing chains ++ std::shared_ptr prev; ++ while (!in.empty()) ++ { ++ auto node = in.pop_front(); ++ local_nodes.back().insert(node); ++ if (prev) edges.insert({prev, node}); ++ prev = node; ++ } ++ if (prev) edges.insert({prev, ep}); ++ ++ prev = ep; ++ while (!out.empty()) ++ { ++ auto node = out.pop_front(); ++ local_nodes.back().insert(node); ++ edges.insert({prev, node}); ++ prev = node; ++ } ++ ++ // then connect the endpoint of those chains to the rest of the network. ++ // Since the network may be arbitrarily complex, we actually have to ++ // completely iterate over all other endpoints ++ ++ for (auto ios2 : io_services) ++ { ++ for (auto const& ip2 : ios2->get_ips()) ++ { ++ route network = s.config().channel_route( ++ ip, ip2); ++ ++ std::shared_ptr last = ios2->get_incoming_route(ip2).next_hop(); ++ ++ prev = egress; ++ while (!network.empty()) ++ { ++ auto node = network.pop_front(); ++ nodes.insert(node); ++ edges.insert({prev, node}); ++ prev = node; ++ } ++ edges.insert({prev, last}); ++ } ++ } ++ } ++ } ++ ++ // by now, the nodes and edges should represent the complete graph. Render it ++ // into dot. ++ ++ FILE* f = fopen(filename.c_str(), "w+"); ++ ++ std::fprintf(f, "digraph network {\n" ++ "concentrate=true;\n" ++ "overlap=scale;\n" ++ "splines=true;\n"); ++ ++ std::fprintf(f, "\n// nodes\n\n"); ++ ++ for (const auto& n : nodes) ++ { ++ std::string attributes = n->attributes(); ++ std::fprintf(f, " \"%p\" [label=\"%s\",style=\"filled\",color=\"red\"%s%s];\n" ++ , static_cast(n.get()) ++ , escape_label(n->label()).c_str() ++ , attributes.empty() ? "" : ", " ++ , attributes.c_str()); ++ } ++ ++ std::fprintf(f, "\n// local networks\n\n"); ++ ++ int idx = 0; ++ for (auto ln : local_nodes) ++ { ++ std::fprintf(f, "subgraph cluster_%d {\n", idx++); ++ ++ for (const auto& n : ln) ++ { ++ std::string attributes = n->attributes(); ++ std::fprintf(f, " \"%p\" [label=\"%s\",style=\"filled\",color=\"green\"%s%s];\n" ++ , static_cast(n.get()) ++ , escape_label(n->label()).c_str() ++ , attributes.empty() ? "" : ", " ++ , attributes.c_str()); ++ } ++ ++ std::fprintf(f, "}\n"); ++ } ++ ++ std::fprintf(f, "\n// edges\n\n"); ++ ++ while (!edges.empty()) ++ { ++ auto edge = *edges.begin(); ++ edges.erase(edges.begin()); ++ ++ std::fprintf(f, "\"%p\" -> \"%p\"\n" ++ , static_cast(edge.first.get()) ++ , static_cast(edge.second.get())); ++ } ++ ++ std::fprintf(f, "}\n"); ++ fclose(f); ++} ++ ++namespace aux { ++ ++ int channel::remote_idx(const asio::ip::tcp::endpoint& self) const ++ { ++ if (ep[0] == self) return 1; ++ if (ep[1] == self) return 0; ++ assert(false && "invalid socket"); ++ return -1; ++ } ++ ++ int channel::self_idx(const asio::ip::tcp::endpoint& self) const ++ { ++ if (ep[0] == self) return 0; ++ if (ep[1] == self) return 1; ++ assert(false && "invalid socket"); ++ return -1; ++ } ++ ++} // aux ++} // sim ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/sink_forwarder.cpp +@@ -0,0 +1,45 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#include "simulator/sink_forwarder.hpp" ++#include "simulator/packet.hpp" ++ ++namespace sim { namespace aux { ++ ++ sink_forwarder::sink_forwarder(sink* dst) ++ : m_dst(dst) ++ {} ++ ++ void sink_forwarder::incoming_packet(packet p) ++ { ++ if (m_dst == nullptr) return; ++ m_dst->incoming_packet(std::move(p)); ++ } ++ ++ std::string sink_forwarder::label() const ++ { ++ return m_dst ? m_dst->label() : ""; ++ } ++ ++ void sink_forwarder::reset(sink* s) ++ { ++ m_dst = s; ++ } ++ ++}} ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/socks_server.cpp +@@ -0,0 +1,1033 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#include "simulator/simulator.hpp" ++#include "simulator/socks_server.hpp" ++ ++#include ++#include ++#include ++#include // for printf ++ ++using namespace sim::asio; ++using namespace sim::asio::ip; ++ ++using boost::system::error_code; ++ ++namespace sim ++{ ++ using namespace aux; ++ ++ socks_server::socks_server(io_context& ios, unsigned short listen_port, int version ++ , std::uint32_t const flags) ++ : m_ios(ios) ++ , m_listen_socket(ios) ++ , m_conn(std::make_shared(m_ios, version, m_cmd_counts, flags, m_bind_port)) ++ , m_version(version) ++ , m_flags(flags) ++ { ++ m_cmd_counts.fill(0); ++ address local_ip = ios.get_ips().front(); ++ if (local_ip.is_v4()) ++ { ++ m_listen_socket.open(tcp::v4()); ++ m_listen_socket.bind(tcp::endpoint(address_v4::any(), listen_port)); ++ } ++ else ++ { ++ m_listen_socket.open(tcp::v6()); ++ m_listen_socket.bind(tcp::endpoint(address_v6::any(), listen_port)); ++ } ++ m_listen_socket.listen(); ++ ++ m_listen_socket.async_accept(m_conn->socket(), m_ep ++ , std::bind(&socks_server::on_accept, this, std::placeholders::_1)); ++ } ++ ++ void socks_server::on_accept(error_code const& ec) ++ { ++ if (ec == asio::error::operation_aborted) ++ return; ++ ++ if (ec) ++ { ++ std::printf("socks_server::on_accept: (%d) %s\n" ++ , ec.value(), ec.message().c_str()); ++ return; ++ } ++ ++ std::printf("socks_server accepted connection from: %s : %d\n", ++ m_ep.address().to_string().c_str(), m_ep.port()); ++ ++ m_conn->start(); ++ ++ // create a new connection to accept into ++ m_conn = std::make_shared(m_ios, m_version, m_cmd_counts, m_flags, m_bind_port); ++ ++ // now we can accept another connection ++ m_listen_socket.async_accept(m_conn->socket(), m_ep ++ , std::bind(&socks_server::on_accept, this, std::placeholders::_1)); ++ } ++ ++ void socks_server::stop() ++ { ++ m_close = true; ++ m_listen_socket.close(); ++ } ++ ++ socks_connection::socks_connection(asio::io_context& ios ++ , int version, std::array& cmd_counts, std::uint32_t const flags, int& bind_port) ++ : m_bind_port(bind_port) ++ , m_ios(ios) ++ , m_udp_resolver(ios) ++ , m_resolver(m_ios) ++ , m_client_connection(ios) ++ , m_server_connection(m_ios) ++ , m_bind_socket(m_ios) ++ , m_udp_associate(m_ios) ++ , m_num_out_bytes(0) ++ , m_num_in_bytes(0) ++ , m_version(version) ++ , m_command(0) ++ , m_cmd_counts(cmd_counts) ++ , m_flags(flags) ++ { ++ } ++ ++ void socks_connection::start() ++ { ++ if (m_version == 4) ++ { ++ asio::async_read(m_client_connection, asio::buffer(&m_out_buffer[0], 9) ++ , std::bind(&socks_connection::on_request1, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); ++ } else { ++ // read protocol version and number of auth-methods ++ asio::async_read(m_client_connection, asio::buffer(&m_out_buffer[0], 2) ++ , std::bind(&socks_connection::on_handshake1, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); ++ } ++ } ++ ++ void socks_connection::on_handshake1(error_code const& ec, size_t bytes_transferred) ++ { ++ if (ec || bytes_transferred != 2) ++ { ++ std::printf("socks_connection::on_handshake1: (%d) %s\n" ++ , ec.value(), ec.message().c_str()); ++ close_connection(); ++ return; ++ } ++ ++ if (m_out_buffer[0] != 4 && m_out_buffer[0] != 5) ++ { ++ std::printf("socks_connection::on_handshake1: unexpected socks protocol version: %d" ++ , int(m_out_buffer[0])); ++ close_connection(); ++ return; ++ } ++ ++ int num_methods = unsigned(m_out_buffer[1]); ++ ++ // read list of auth-methods ++ asio::async_read(m_client_connection, asio::buffer(&m_out_buffer[0], ++ num_methods) ++ , std::bind(&socks_connection::on_handshake2, shared_from_this() ++ , std::placeholders::_1, std::placeholders::_2)); ++ } ++ ++ void socks_connection::on_handshake2(error_code const& ec, size_t bytes_transferred) ++ { ++ if (ec) ++ { ++ std::printf("socks_connection::on_handshake2: (%d) %s\n" ++ , ec.value(), ec.message().c_str()); ++ close_connection(); ++ return; ++ } ++ ++ if (std::count(m_out_buffer, m_out_buffer + bytes_transferred, 0) == 0) ++ { ++ std::printf("socks_connection: could not find auth-method 0 (no-auth) in socks handshake\n"); ++ close_connection(); ++ return; ++ } ++ ++ m_in_buffer[0] = 5; // socks version ++ m_in_buffer[1] = 0; // auth-method (no-auth) ++ ++ asio::async_write(m_client_connection, asio::buffer(&m_in_buffer[0], 2) ++ , std::bind(&socks_connection::on_handshake3, shared_from_this() ++ , std::placeholders::_1, std::placeholders::_2)); ++ } ++ ++ void socks_connection::on_handshake3(error_code const& ec, size_t bytes_transferred) ++ { ++ if (ec || bytes_transferred != 2) ++ { ++ std::printf("socks_connection::on_handshake3: (%d) %s\n" ++ , ec.value(), ec.message().c_str()); ++ close_connection(); ++ return; ++ } ++ ++ asio::async_read(m_client_connection, asio::buffer(&m_out_buffer[0], 10) ++ , std::bind(&socks_connection::on_request1, shared_from_this() ++ , std::placeholders::_1, std::placeholders::_2)); ++ } ++ ++ void socks_connection::on_request1(error_code const& ec, size_t bytes_transferred) ++ { ++ size_t const expected = m_version == 4 ? 9 : 10; ++ if (ec || bytes_transferred != expected) ++ { ++ std::printf("socks_connection::on_request1: (%d) %s\n" ++ , ec.value(), ec.message().c_str()); ++ close_connection(); ++ return; ++ } ++ ++// +----+-----+-------+------+----------+----------+ ++// |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | ++// +----+-----+-------+------+----------+----------+ ++// | 1 | 1 | X'00' | 1 | Variable | 2 | ++// +----+-----+-------+------+----------+----------+ ++ ++ int const version = m_out_buffer[0]; ++ int const command = m_out_buffer[1]; ++ m_command = command; ++ ++m_cmd_counts[command - 1]; ++ ++ if (version != m_version) ++ { ++ std::printf("socks_connection::on_request1: unexpected socks protocol version: %d expected: %d\n" ++ , int(m_out_buffer[0]), m_version); ++ close_connection(); ++ return; ++ } ++ ++ if (m_version == 4) ++ { ++ if (command != 1 && command != 2) ++ { ++ std::printf("socks_connection::on_request1: unexpected socks command: %d\n" ++ , command); ++ close_connection(); ++ return; ++ } ++ ++ std::uint16_t port = m_out_buffer[2] & 0xff; ++ port <<= 8; ++ port |= m_out_buffer[3] & 0xff; ++ ++ std::uint32_t addr = m_out_buffer[4] & 0xff; ++ addr <<= 8; ++ addr |= m_out_buffer[5] & 0xff; ++ addr <<= 8; ++ addr |= m_out_buffer[6] & 0xff; ++ addr <<= 8; ++ addr |= m_out_buffer[7] & 0xff; ++ ++ if (m_out_buffer[8] != 0) ++ { ++ // in this case, we would have to read one byte at a time until we ++ // get to the null terminator. ++ std::printf("socks_connection::on_request1: username in SOCKS4 mode not supported\n"); ++ close_connection(); ++ return; ++ } ++ asio::ip::tcp::endpoint target(asio::ip::address_v4(addr), port); ++ if (command == 1) ++ { ++ open_forward_connection(target); ++ } ++ else if (command == 2) ++ { ++ bind_connection(target); ++ } ++ ++ return; ++ } ++ ++ if (command != 1 && command != 2 && command != 3) ++ { ++ std::printf("socks_connection::on_request1: unexpected command: %d\n" ++ , command); ++ close_connection(); ++ return; ++ } ++ ++ if (m_out_buffer[2] != 0) ++ { ++ std::printf("socks_connection::on_request1: reserved byte is non-zero: %d\n" ++ , int(m_out_buffer[2])); ++ close_connection(); ++ return; ++ } ++ ++ int atyp = unsigned(m_out_buffer[3]); ++ ++ if (atyp != 1 && atyp != 3 && atyp != 4) ++ { ++ std::printf("socks_connection::on_request1: unexpected address type in SOCKS request: %d\n" ++ , atyp); ++ close_connection(); ++ return; ++ } ++ ++ std::printf("socks_connection: received %s request address type: %d\n" ++ , command == 1 ? "CONNECT" ++ : command == 2 ? "BIND" ++ : "UDP_ASSOCIATE", atyp); ++ ++ switch (atyp) ++ { ++ case 1: { // IPv4 address (we have the whole request already) ++ ++// +----+-----+-------+------+----------+----------+ ++// |VER | CMD | RSV | ATYP | BND.ADDR | BND.PORT | ++// +----+-----+-------+------+----------+----------+ ++// | 1 | 1 | X'00' | 1 | 4 | 2 | ++// +----+-----+-------+------+----------+----------+ ++ ++ std::uint32_t addr = m_out_buffer[4] & 0xff; ++ addr <<= 8; ++ addr |= m_out_buffer[5] & 0xff; ++ addr <<= 8; ++ addr |= m_out_buffer[6] & 0xff; ++ addr <<= 8; ++ addr |= m_out_buffer[7] & 0xff; ++ ++ std::uint16_t port = m_out_buffer[8] & 0xff; ++ port <<= 8; ++ port |= m_out_buffer[9] & 0xff; ++ ++ asio::ip::tcp::endpoint target(asio::ip::address_v4(addr), port); ++ if (command == 1) ++ { ++ open_forward_connection(target); ++ } ++ else if (command == 2) ++ { ++ bind_connection(target); ++ } ++ else if (command == 3) ++ { ++ if (target.address() == address()) ++ { ++ target.address(m_client_connection.remote_endpoint().address()); ++ } ++ udp_associate(target); ++ } ++ ++ break; ++ } ++ case 3: { // domain name ++ ++// +----+-----+-------+------+-----+----------+----------+ ++// |VER | CMD | RSV | ATYP | LEN | BND.ADDR | BND.PORT | ++// +----+-----+-------+------+-----+----------+----------+ ++// | 1 | 1 | X'00' | 1 | 1 | Variable | 2 | ++// +----+-----+-------+------+-----+----------+----------+ ++ ++ if (command == 2) ++ { ++ std::printf("ERROR: cannot BIND to hostname address (only IPv4 or IPv6 addresses)\n"); ++ close_connection(); ++ return; ++ } ++ ++ const int len = std::uint8_t(m_out_buffer[4]); ++ // we already read an address of length 4, assuming it was an IPv4 ++ // address. Now, with a domain name, one of those bytes was the ++ // length-prefix, but we still read 3 bytes already. ++ const int additional_bytes = len - 3; ++ asio::async_read(m_client_connection, asio::buffer(&m_out_buffer[10], additional_bytes) ++ , std::bind(&socks_connection::on_request_domain_name ++ , shared_from_this(), std::placeholders::_1, std::placeholders::_2)); ++ break; ++ } ++ case 4: // IPv6 address ++ ++// +----+-----+-------+------+----------+----------+ ++// |VER | CMD | RSV | ATYP | BND.ADDR | BND.PORT | ++// +----+-----+-------+------+----------+----------+ ++// | 1 | 1 | X'00' | 1 | 16 | 2 | ++// +----+-----+-------+------+----------+----------+ ++ ++ std::printf("ERROR: unsupported address type %d\n", atyp); ++ close_connection(); ++ } ++ } ++ ++ void socks_connection::on_request_domain_name(error_code const& ec, size_t bytes_transferred) ++ { ++ if (ec) ++ { ++ std::printf("socks_connection::on_request_domain_name(%s): (%d) %s\n" ++ , command(), ec.value(), ec.message().c_str()); ++ close_connection(); ++ return; ++ } ++ ++ int const buffer_size = int(10 + bytes_transferred); ++ ++ std::uint16_t port = m_out_buffer[buffer_size - 2] & 0xff; ++ port <<= 8; ++ port |= m_out_buffer[buffer_size - 1] & 0xff; ++ ++ std::string hostname(&m_out_buffer[5], std::uint8_t(m_out_buffer[4])); ++ std::printf("socks_connection::on_request_domain_name(%s): hostname: %s port: %d\n" ++ , command(), hostname.c_str(), port); ++ ++ char port_str[10]; ++ std::snprintf(port_str, sizeof(port_str), "%d", port); ++ m_resolver.async_resolve(hostname, port_str ++ , std::bind(&socks_connection::on_request_domain_lookup ++ , shared_from_this(), std::placeholders::_1, std::placeholders::_2)); ++ } ++ ++ void socks_connection::on_request_domain_lookup(boost::system::error_code const& ec ++ , asio::ip::tcp::resolver::results_type const ips) ++ { ++ if (ec || ips.empty()) ++ { ++ if (ec) ++ { ++ std::printf("socks_connection::on_request_domain_lookup(%s): (%d) %s\n" ++ , command(), ec.value(), ec.message().c_str()); ++ } ++ else ++ { ++ std::printf("socks_connection::on_request_domain_lookup(%s): empty response\n" ++ , command()); ++ } ++ ++// +----+-----+-------+------+----------+----------+ ++// |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | ++// +----+-----+-------+------+----------+----------+ ++// | 1 | 1 | X'00' | 1 | Variable | 2 | ++// +----+-----+-------+------+----------+----------+ ++ ++ m_in_buffer[0] = char(m_version); // version ++ m_in_buffer[1] = 4; // response (host unreachable) ++ m_in_buffer[2] = 0; // reserved ++ m_in_buffer[3] = 1; // IPv4 ++ memset(&m_in_buffer[4], 0, 4); ++ m_in_buffer[8] = 0; // port ++ m_in_buffer[9] = 0; ++ ++ auto self = shared_from_this(); ++ asio::async_write(m_client_connection ++ , asio::buffer(&m_in_buffer[0], 10) ++ , [=](boost::system::error_code const&, size_t) ++ { ++ self->close_connection(); ++ }); ++ return; ++ } ++ ++ std::printf("socks_connection::on_request_domain_lookup(%s): connecting to: %s port: %d\n" ++ , command() ++ , ips.front().endpoint().address().to_string().c_str() ++ , ips.front().endpoint().port()); ++ open_forward_connection(ips.front().endpoint()); ++ } ++ ++ void socks_connection::open_forward_connection(const asio::ip::tcp::endpoint& target) ++ { ++ std::printf("socks_connection::open_forward_connection(%s): connecting to %s port %d\n" ++ , command(), target.address().to_string().c_str(), target.port()); ++ ++ m_server_connection.open(target.protocol()); ++ m_server_connection.async_connect(target ++ , std::bind(&socks_connection::on_connected, shared_from_this() ++ , std::placeholders::_1)); ++ } ++ ++ void socks_connection::bind_connection(const asio::ip::tcp::endpoint& target) ++ { ++ std::printf("socks_connection::bind_connection(%s): binding to %s port %d\n" ++ , command(), target.address().to_string().c_str(), target.port()); ++ ++ error_code ec; ++ m_bind_socket.open(target.protocol(), ec); ++ if (ec) ++ { ++ std::printf("ERROR: open bind socket failed: (%d) %s\n", ec.value() ++ , ec.message().c_str()); ++ } ++ else ++ { ++ m_bind_socket.bind(target, ec); ++ } ++ ++ int const response = ec ++ ? (m_version == 4 ? 91 : 1) ++ : (m_version == 4 ? 90 : 0); ++ tcp::endpoint ep = m_bind_socket.local_endpoint(); ++ int const len = format_response(ep.address(), ep.port(), response); ++ ++ if (ec) ++ { ++ std::printf("ERROR: binding socket to %s %d failed: (%d) %s\n" ++ , target.address().to_string().c_str() ++ , target.port() ++ , ec.value() ++ , ec.message().c_str()); ++ ++ auto self = shared_from_this(); ++ ++ asio::async_write(m_client_connection ++ , asio::buffer(&m_in_buffer[0], len) ++ , [=](boost::system::error_code const&, size_t) ++ { ++ self->close_connection(); ++ }); ++ return; ++ } ++ ++ // send response ++ asio::async_write(m_client_connection ++ , asio::buffer(&m_in_buffer[0], len) ++ , std::bind(&socks_connection::start_accept, shared_from_this(), std::placeholders::_1)); ++ } ++ ++ void socks_connection::udp_associate(const asio::ip::tcp::endpoint& target) ++ { ++ std::printf("socks_connection::udp_associate(%s): %s:%d\n" ++ , command(), target.address().to_string().c_str(), target.port()); ++ ++ m_udp_associate_ep.address(target.address()); ++ m_udp_associate_ep.port(target.port()); ++ ++ error_code ec; ++ m_udp_associate.open(m_udp_associate_ep.protocol(), ec); ++ if (ec) ++ { ++ std::printf("ERROR: open UDP associate socket failed: (%d) %s\n", ec.value() ++ , ec.message().c_str()); ++ } ++ else ++ { ++ m_udp_associate.bind(udp::endpoint(address_v4(), std::uint16_t(m_bind_port++)), ec); ++ if (ec) ++ { ++ std::printf("ERROR: binding socket failed: (%d) %s\n" ++ , ec.value(), ec.message().c_str()); ++ } ++ else ++ { ++ m_udp_associate.non_blocking(true); ++ m_udp_associate.async_receive_from(boost::asio::buffer(m_udp_buffer) ++ , m_udp_from, 0, std::bind(&socks_connection::on_read_udp, this, std::placeholders::_1, std::placeholders::_2)); ++ } ++ } ++ ++ int const response = ec ? 1 : 0; ++ udp::endpoint ep = m_udp_associate.local_bound_to(); ++ int const len = (m_flags & udp_associate_respond_empty_hostname) ++ ? format_hostname_response("foobar", ep.port(), response) ++ : format_response(ep.address(), ep.port(), response); ++ ++ if (ec) ++ { ++ auto self = shared_from_this(); ++ ++ asio::async_write(m_client_connection ++ , asio::buffer(&m_in_buffer[0], len) ++ , [=](boost::system::error_code const&, size_t) ++ { ++ self->close_connection(); ++ }); ++ return; ++ } ++ ++ // send response ++ asio::async_write(m_client_connection, asio::buffer(&m_in_buffer[0], len) ++ , std::bind(&socks_connection::wait_for_eof, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); ++ } ++ ++ void socks_connection::wait_for_eof(boost::system::error_code const& ec, std::size_t) ++ { ++ if (ec) ++ { ++ std::printf("socks_connection::wait_for_eof: %s\n", ec.message().c_str()); ++ m_udp_associate.close(); ++ m_udp_associate_ep = udp::endpoint(); ++ m_client_connection.close(); ++ return; ++ } ++ ++ if (m_flags & socks_flag::disconnect_udp_associate) ++ { ++ std::printf("socks_connection::wait_for_eof: closing connection prematurely\n"); ++ m_client_connection.close(); ++ return; ++ } ++ ++ m_client_connection.async_read_some( ++ asio::buffer(m_out_buffer) ++ , std::bind(&socks_connection::wait_for_eof, shared_from_this() ++ , std::placeholders::_1, std::placeholders::_2)); ++ } ++ ++ void socks_connection::on_read_udp(boost::system::error_code const& ec ++ , std::size_t bytes_transferred) ++ { ++ std::printf("socks_connection::on_read_udp\n"); ++ if (ec) ++ { ++ std::printf("socks_connection::on_read_udp: %s\n", ec.message().c_str()); ++ return; ++ } ++ ++ // if the client didn't specify an IP and port it would send packets from, ++ // we assumed the same IP as the TCP connection and assume the port is the ++ // same as the first UDP packet from that host ++ if (m_udp_associate_ep.port() == 0 ++ && m_udp_from.address() == m_udp_associate_ep.address()) ++ { ++ m_udp_associate_ep.port(m_udp_from.port()); ++ } ++ ++ if (m_udp_from == m_udp_associate_ep) ++ { ++ // read UDP ASSICOATE header and forward outgoing packet ++ // +----+------+------+----------+----------+----------+ ++ // |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | ++ // +----+------+------+----------+----------+----------+ ++ // | 2 | 1 | 1 | Variable | 2 | Variable | ++ // +----+------+------+----------+----------+----------+ ++ ++ char const* buf = m_udp_buffer.data(); ++ if (buf[2] != 0) std::printf("fragment != 0, not supported\n"); ++ ++ int const atyp = buf[3]; ++ if (atyp == 3) ++ { ++ // hostname ++ int const len = buf[4]; ++ ++ buf += 5; ++ bytes_transferred -= 5; ++ std::string const hostname(buf, len); ++ buf += len; ++ bytes_transferred -= len; ++ ++ std::uint16_t port = buf[0] & 0xff; ++ port <<= 8; ++ port |= buf[1] & 0xff; ++ buf += 2; ++ bytes_transferred -= 2; ++ ++ auto it = m_name_mapping.right.find(hostname); ++ if (it != m_name_mapping.right.end()) ++ { ++ error_code err; ++ m_udp_associate.send_to(boost::asio::buffer(buf, bytes_transferred) ++ , udp::endpoint(it->second, port), 0, err); ++ if (err) std::printf("send_to failed: %s\n", err.message().c_str()); ++ return; ++ } ++ ++ std::vector forward_buffer(buf, buf + bytes_transferred); ++ ++ m_udp_resolver.async_resolve(hostname.c_str(), std::to_string(port).c_str() ++ , [buf=std::move(forward_buffer), hostname, this] ++ (error_code const& ec, asio::ip::udp::resolver::results_type ips) ++ { ++ if (ec) ++ { ++ std::printf("resolve failed: %s\n", ec.message().c_str()); ++ return; ++ } ++ ++ for (auto const& ip : ips) ++ { ++ auto const target = ip.endpoint(); ++ error_code err; ++ m_udp_associate.send_to(boost::asio::buffer(buf) ++ , target, 0, err); ++ if (!err) ++ { ++ m_name_mapping.insert({target.address(), hostname}); ++ break; ++ } ++ std::printf("send_to failed: %s\n", err.message().c_str()); ++ } ++ }); ++ } ++ else if (atyp == 1) ++ { ++ // IPv4 ++ std::uint32_t addr = buf[4] & 0xff; ++ addr <<= 8; ++ addr |= buf[5] & 0xff; ++ addr <<= 8; ++ addr |= buf[6] & 0xff; ++ addr <<= 8; ++ addr |= buf[7] & 0xff; ++ ++ std::uint16_t port = buf[8] & 0xff; ++ port <<= 8; ++ port |= buf[9] & 0xff; ++ ++ buf += 10; ++ bytes_transferred -= 10; ++ ++ asio::ip::udp::endpoint const target(address_v4(addr), port); ++ ++ error_code err; ++ m_udp_associate.send_to(boost::asio::buffer(buf, bytes_transferred), target, 0, err); ++ if (err) std::printf("send_to failed: %s\n", err.message().c_str()); ++ } ++ else ++ { ++ std::printf("only supports IPv4 and hostname. ATYP: %d\n", atyp); ++ } ++ } ++ else ++ { ++ std::uint16_t const from_port = m_udp_from.port(); ++ ++ auto it = m_name_mapping.left.find(m_udp_from.address()); ++ if (it != m_name_mapping.left.end()) ++ { ++ std::vector header(7 + it->second.size()); ++ header[0] = 0; // RSV ++ header[1] = 0; ++ header[2] = 0; // fragment ++ header[3] = 3; // ATYP ++ header[4] = static_cast(it->second.size()); ++ int idx = 5; ++ std::copy(it->second.begin(), it->second.end(), header.data() + idx); ++ idx += static_cast(it->second.size()); ++ header[idx] = (from_port >> 8) & 0xff; ++ header[idx + 1] = from_port & 0xff; ++ ++ std::array vec{{ ++ {header.data(), header.size()}, ++ {m_udp_buffer.data(), bytes_transferred}}}; ++ ++ error_code err; ++ m_udp_associate.send_to(vec, m_udp_associate_ep, 0, err); ++ if (err) std::printf("send_to failed: %s\n", err.message().c_str()); ++ } ++ else ++ { ++ // add UDP ASSOCIATE header and forward to client ++ std::uint32_t const from_addr = m_udp_from.address().to_v4().to_uint(); ++ std::array header; ++ header[0] = 0; // RSV ++ header[1] = 0; ++ header[2] = 0; // fragment ++ header[3] = 1; // ATYP ++ header[4] = (from_addr >> 24) & 0xff; // Address ++ header[5] = (from_addr >> 16) & 0xff; ++ header[6] = (from_addr >> 8) & 0xff; ++ header[7] = (from_addr) & 0xff; ++ header[8] = (from_port >> 8) & 0xff; ++ header[9] = from_port & 0xff; ++ ++ std::array vec{{ ++ {header.data(), header.size()}, ++ {m_udp_buffer.data(), bytes_transferred}}}; ++ ++ error_code err; ++ m_udp_associate.send_to(vec, m_udp_associate_ep, 0, err); ++ if (err) std::printf("send_to failed: %s\n", err.message().c_str()); ++ } ++ } ++ ++ m_udp_associate.async_receive_from(boost::asio::buffer(m_udp_buffer) ++ , m_udp_from, 0, std::bind(&socks_connection::on_read_udp, this, std::placeholders::_1, std::placeholders::_2)); ++ } ++ ++ void socks_connection::start_accept(boost::system::error_code const& ec) ++ { ++ if (ec) ++ { ++ std::printf("socks_connection(%s): error writing to client: (%d) %s\n" ++ , command(), ec.value(), ec.message().c_str()); ++ close_connection(); ++ return; ++ } ++ ++ m_bind_socket.listen(); ++ m_bind_socket.async_accept(m_server_connection ++ , std::bind(&socks_connection::on_connected ++ , shared_from_this(), std::placeholders::_1)); ++ ++ m_client_connection.async_read_some( ++ sim::asio::buffer(m_out_buffer) ++ , std::bind(&socks_connection::on_client_receive, shared_from_this() ++ , std::placeholders::_1, std::placeholders::_2)); ++ } ++ ++ int socks_connection::format_response(address const& addr, int const port ++ , int const response) ++ { ++ int i = 0; ++ if (m_version == 5) ++ { ++// +----+-----+-------+------+----------+----------+ ++// |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | ++// +----+-----+-------+------+----------+----------+ ++// | 1 | 1 | X'00' | 1 | Variable | 2 | ++// +----+-----+-------+------+----------+----------+ ++ ++ m_in_buffer[i++] = char(m_version); // version ++ m_in_buffer[i++] = char(response); // response ++ m_in_buffer[i++] = 0; // reserved ++ if (addr.is_v4()) ++ { ++ m_in_buffer[i++] = 1; // IPv4 ++ address_v4::bytes_type b = addr.to_v4().to_bytes(); ++ memcpy(&m_in_buffer[i], &b[0], b.size()); ++ i += int(b.size()); ++ } else { ++ m_in_buffer[i++] = 4; // IPv6 ++ address_v6::bytes_type b = addr.to_v6().to_bytes(); ++ memcpy(&m_in_buffer[i], &b[0], b.size()); ++ i += int(b.size()); ++ } ++ ++ m_in_buffer[i++] = (port >> 8) & 0xff; ++ m_in_buffer[i++] = port & 0xff; ++ } ++ else ++ { ++ m_in_buffer[i++] = 0; // response version ++ m_in_buffer[i++] = char(response); // return code ++ ++ assert(addr.is_v4()); ++ ++ m_in_buffer[i++] = (port >> 8) & 0xff; ++ m_in_buffer[i++] = port & 0xff; ++ ++ address_v4::bytes_type b = addr.to_v4().to_bytes(); ++ memcpy(&m_in_buffer[i], &b[0], b.size()); ++ i += int(b.size()); ++ } ++ return i; ++ } ++ ++ int socks_connection::format_hostname_response(char const* hostname, int const port ++ , int const response) ++ { ++ int i = 0; ++ if (m_version != 5) ++ { ++ std::printf("socks_connection: hostname response requires SOCKS v5\n"); ++ close_connection(); ++ return 0; ++ } ++// +----+-----+-------+------+----------+----------+ ++// |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | ++// +----+-----+-------+------+----------+----------+ ++// | 1 | 1 | X'00' | 1 | Variable | 2 | ++// +----+-----+-------+------+----------+----------+ ++ ++ m_in_buffer[i++] = char(m_version); // version ++ m_in_buffer[i++] = char(response); // response ++ m_in_buffer[i++] = 0; // reserved ++ m_in_buffer[i++] = 3; // DOMAINNAME ++ m_in_buffer[i++] = std::uint8_t(::strlen(hostname)); ++ for (; *hostname != '\0'; ++hostname) ++ m_in_buffer[i++] = *hostname; ++ ++ m_in_buffer[i++] = (port >> 8) & 0xff; ++ m_in_buffer[i++] = port & 0xff; ++ return i; ++ } ++ ++ void socks_connection::on_connected(boost::system::error_code const& ec) ++ { ++ std::printf("socks_connection(%s): on_connect: (%d) %s\n" ++ , command(), ec.value(), ec.message().c_str()); ++ ++ if (ec == asio::error::operation_aborted ++ || ec == boost::system::errc::bad_file_descriptor) ++ { ++ return; ++ } ++ ++ boost::system::error_code err; ++ asio::ip::tcp::endpoint const ep = m_server_connection.remote_endpoint(err); ++ if (!err) ++ { ++ std::printf("socks_connection(%s): remote_endpoint: %s %d\n" ++ , command(), ep.address().to_string().c_str(), ep.port()); ++ } ++ ++ int const response = ec ++ ? (m_version == 4 ? 91 : 5) ++ : (m_version == 4 ? 90 : 0); ++ int const len = format_response(ep.address(), ep.port(), response); ++ ++ if (ec) ++ { ++ std::printf("socks_connection(%s): failed to connect to/accept from target server: (%d) %s\n" ++ , command(), ec.value(), ec.message().c_str()); ++ ++ auto self = shared_from_this(); ++ ++ asio::async_write(m_client_connection ++ , asio::buffer(&m_in_buffer[0], len) ++ , [=](boost::system::error_code const&, size_t) ++ { ++ self->close_connection(); ++ }); ++ return; ++ } ++ ++ auto self = shared_from_this(); ++ ++ asio::async_write(m_client_connection ++ , asio::buffer(&m_in_buffer[0], len) ++ , [this, self](boost::system::error_code const& ec, size_t) ++ { ++ if (ec) ++ { ++ std::printf("socks_connection(%s): error writing to client: (%d) %s\n" ++ , command(), ec.value(), ec.message().c_str()); ++ return; ++ } ++ ++ // read from the client and from the server ++ self->m_server_connection.async_read_some( ++ sim::asio::buffer(m_in_buffer) ++ , std::bind(&socks_connection::on_server_receive, self ++ , std::placeholders::_1, std::placeholders::_2)); ++ self->m_client_connection.async_read_some( ++ sim::asio::buffer(m_out_buffer) ++ , std::bind(&socks_connection::on_client_receive, self ++ , std::placeholders::_1, std::placeholders::_2)); ++ }); ++ } ++ ++ // we received some data from the client, forward it to the server ++ void socks_connection::on_client_receive(boost::system::error_code const& ec ++ , std::size_t bytes_transferred) ++ { ++ // bad file descriptor means the socket has been closed. Whoever closed ++ // the socket will have opened a new one, we cannot call ++ // close_connection() ++ if (ec == asio::error::operation_aborted ++ || ec == boost::system::errc::bad_file_descriptor) ++ return; ++ ++ if (ec) ++ { ++ std::printf("socks_connection (%s): error reading from client: (%d) %s\n" ++ , command(), ec.value(), ec.message().c_str()); ++ close_connection(); ++ return; ++ } ++ asio::async_write(m_server_connection, asio::buffer(&m_out_buffer[0], bytes_transferred) ++ , std::bind(&socks_connection::on_client_forward, shared_from_this() ++ , std::placeholders::_1, std::placeholders::_2)); ++ } ++ ++ void socks_connection::on_client_forward(error_code const& ec ++ , size_t /* bytes_transferred */) ++ { ++ if (ec) ++ { ++ std::printf("socks_connection(%s): error writing to server: (%d) %s\n" ++ , command(), ec.value(), ec.message().c_str()); ++ close_connection(); ++ return; ++ } ++ ++ m_client_connection.async_read_some( ++ sim::asio::buffer(m_out_buffer) ++ , std::bind(&socks_connection::on_client_receive, shared_from_this() ++ , std::placeholders::_1, std::placeholders::_2)); ++ } ++ ++ // we received some data from the server, forward it to the server ++ void socks_connection::on_server_receive(boost::system::error_code const& ec ++ , std::size_t bytes_transferred) ++ { ++ if (ec) ++ { ++ std::printf("socks_connection(%s): error reading from server: (%d) %s\n" ++ , command(), ec.value(), ec.message().c_str()); ++ close_connection(); ++ return; ++ } ++ ++ asio::async_write(m_client_connection, asio::buffer(&m_in_buffer[0], bytes_transferred) ++ , std::bind(&socks_connection::on_server_forward, shared_from_this() ++ , std::placeholders::_1, std::placeholders::_2)); ++ } ++ ++ void socks_connection::on_server_forward(error_code const& ec ++ , size_t /* bytes_transferred */) ++ { ++ if (ec) ++ { ++ std::printf("socks_connection(%s): error writing to client: (%d) %s\n" ++ , command(), ec.value(), ec.message().c_str()); ++ close_connection(); ++ return; ++ } ++ ++ m_server_connection.async_read_some( ++ sim::asio::buffer(m_in_buffer) ++ , std::bind(&socks_connection::on_server_receive, shared_from_this() ++ , std::placeholders::_1, std::placeholders::_2)); ++ } ++ ++ void socks_connection::close_connection() ++ { ++ error_code err; ++ m_client_connection.close(err); ++ if (err) ++ { ++ std::printf("socks_connection::close: failed to close client connection (%d) %s\n" ++ , err.value(), err.message().c_str()); ++ } ++ m_server_connection.close(err); ++ if (err) ++ { ++ std::printf("socks_connection::close: failed to close server connection (%d) %s\n" ++ , err.value(), err.message().c_str()); ++ } ++ ++ m_bind_socket.close(err); ++ if (err) ++ { ++ std::printf("socks_connection::close: failed to close bind socket (%d) %s\n" ++ , err.value(), err.message().c_str()); ++ } ++ } ++ ++ char const* socks_connection::command() const ++ { ++ switch (m_command) ++ { ++ case 1: return "CONNECT"; ++ case 2: return "BIND"; ++ case 3: return "UDP_ASSOCIATE"; ++ default: return "UNKNOWN"; ++ } ++ } ++} ++ ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/tcp_socket.cpp +@@ -0,0 +1,929 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#include "simulator/simulator.hpp" ++#include "simulator/packet.hpp" ++#include "simulator/pcap.hpp" ++#include "simulator/handler_allocator.hpp" ++ ++#include ++#include ++#include // for printf ++ ++#include "simulator/push_warnings.hpp" ++#include ++#include ++#include "simulator/pop_warnings.hpp" ++ ++typedef sim::chrono::high_resolution_clock::time_point time_point; ++typedef sim::chrono::high_resolution_clock::duration duration; ++ ++using namespace std::placeholders; ++ ++namespace sim { ++namespace asio { ++namespace ip { ++ ++ tcp::socket::socket(io_context& ios) ++ : socket_base(ios) ++ , m_connect_timer(ios) ++ , m_recv_timer(ios) ++ {} ++ ++ tcp::socket::socket(socket&& s) ++ : socket_base(std::move(s)) ++ , m_connect_handler(std::move(s.m_connect_handler)) ++ , m_connect_timer(std::move(s.m_connect_timer)) ++ , m_mss(s.m_mss) ++ , m_send_handler(std::move(s.m_send_handler)) ++ , m_wait_send_handler(std::move(s.m_wait_send_handler)) ++ , m_send_buffer(std::move(s.m_send_buffer)) ++ , m_incoming_queue(std::move(s.m_incoming_queue)) ++ , m_queue_size(std::move(s.m_queue_size)) ++ , m_recv_handler(std::move(s.m_recv_handler)) ++ , m_wait_recv_handler(std::move(s.m_wait_recv_handler)) ++ , m_recv_buffer(std::move(s.m_recv_buffer)) ++ , m_recv_timer(std::move(s.m_recv_timer)) ++ , m_is_v4(std::move(s.m_is_v4)) ++ , m_recv_null_buffers(std::move(s.m_recv_null_buffers)) ++ , m_send_null_buffers(std::move(s.m_send_null_buffers)) ++ , m_channel(std::move(s.m_channel)) ++ , m_next_outgoing_seq(std::move(s.m_next_outgoing_seq)) ++ , m_next_incoming_seq(std::move(s.m_next_incoming_seq)) ++ , m_last_drop_seq(std::move(s.m_last_drop_seq)) ++ , m_cwnd(std::move(s.m_cwnd)) ++ , m_bytes_in_flight(std::move(s.m_bytes_in_flight)) ++ , m_reorder_buffer(std::move(s.m_reorder_buffer)) ++ , m_outstanding_packet_sizes(std::move(s.m_outstanding_packet_sizes)) ++ , m_outgoing_packets(std::move(s.m_outgoing_packets)) ++ { ++ if (m_forwarder) m_forwarder->reset(this); ++ s.m_forwarder.reset(); ++ s.m_open = false; ++ s.m_bound_to = ip::tcp::endpoint(); ++ ++ if (m_bound_to != ip::tcp::endpoint()) ++ m_io_service.rebind_socket(&s, this, m_bound_to); ++ } ++ ++ tcp::socket::~socket() ++ { ++ boost::system::error_code ec; ++ ++ m_channel.reset(); ++ ++ if (m_bound_to != ip::tcp::endpoint()) ++ { ++ m_io_service.unbind_socket(this, m_bound_to); ++ m_bound_to = ip::tcp::endpoint(); ++ m_user_bound_to = ip::tcp::endpoint(); ++ } ++ m_open = false; ++ ++ // prevent any more packets from being delivered to this socket ++ if (m_forwarder) ++ { ++ m_forwarder->reset(); ++ m_forwarder.reset(); ++ } ++ cancel(ec); ++ } ++ ++ void tcp::socket::open(tcp protocol, boost::system::error_code& ec) try ++ { ++ close(ec); ++ m_open = true; ++ m_is_v4 = (protocol == ip::tcp::v4()); ++ ec.clear(); ++ m_forwarder = std::make_shared(this); ++ } ++ catch (std::bad_alloc const&) ++ { ++ ec = make_error_code(boost::system::errc::not_enough_memory); ++ } ++ catch (boost::system::system_error const& err) ++ { ++ ec = err.code(); ++ } ++ ++ void tcp::socket::open(tcp protocol) ++ { ++ boost::system::error_code ec; ++ open(protocol, ec); ++ if (ec) throw boost::system::system_error(ec); ++ } ++ ++ // used to attach an incoming connection to this ++ void tcp::socket::internal_connect(tcp::endpoint const& bind_ip ++ , std::shared_ptr const& c ++ , boost::system::error_code& ec) ++ { ++ open(m_is_v4 ? tcp::v4() : tcp::v6(), ec); ++ if (ec) ++ { ++ std::printf("tcp::socket::internal_connect() error: (%d) %s\n" ++ , ec.value(), ec.message().c_str()); ++ return; ++ } ++ m_bound_to = bind_ip; ++ m_user_bound_to = bind_ip; ++ m_channel = c; ++ assert(m_forwarder); ++ c->hops[1].replace_last(m_forwarder); ++ } ++ ++ void tcp::socket::bind(ip::tcp::endpoint const& ep ++ , boost::system::error_code& ec) try ++ { ++ if (!m_open) ++ { ++ ec = error::bad_descriptor; ++ return; ++ } ++ ++ if (ep.address().is_v4() != m_is_v4) ++ { ++ ec = error::address_family_not_supported; ++ return; ++ } ++ ++ ip::tcp::endpoint addr = m_io_service.bind_socket(this, ep, ec); ++ if (ec) return; ++ m_bound_to = addr; ++ m_user_bound_to = addr; ++ } ++ catch (std::bad_alloc const&) ++ { ++ ec = make_error_code(boost::system::errc::not_enough_memory); ++ } ++ catch (boost::system::system_error const& err) ++ { ++ ec = err.code(); ++ } ++ ++ void tcp::socket::bind(ip::tcp::endpoint const& ep) ++ { ++ boost::system::error_code ec; ++ bind(ep, ec); ++ if (ec) throw boost::system::system_error(ec); ++ } ++ ++ void tcp::socket::close() ++ { ++ boost::system::error_code ec; ++ close(ec); ++ if (ec) throw boost::system::system_error(ec); ++ } ++ ++ void tcp::socket::close(boost::system::error_code& ec) try ++ { ++ if (m_channel) ++ { ++ int const remote = m_channel->remote_idx(m_bound_to); ++ route hops = m_channel->hops[remote]; ++ ++ // if m_connect_handler is still set, it means the connection hasn't ++ // been established yet, and this channel points to the acceptor ++ // socket, not another open TCP connection. ++ if (!hops.empty() && !m_connect_handler) ++ { ++ aux::packet p; ++ p.type = aux::packet::type_t::error; ++ p.ec = asio::error::eof; ++ p.from = asio::ip::udp::endpoint( ++ m_bound_to.address(), m_bound_to.port()); ++ p.overhead = 40; ++ p.hops = hops; ++ p.seq_nr = m_next_outgoing_seq++; ++ send_packet(std::move(p)); ++ } ++ m_channel.reset(); ++ } ++ ++ if (m_bound_to != ip::tcp::endpoint()) ++ { ++ m_io_service.unbind_socket(this, m_bound_to); ++ m_bound_to = ip::tcp::endpoint(); ++ m_user_bound_to = ip::tcp::endpoint(); ++ } ++ m_open = false; ++ ++ // prevent any more packets from being delivered to this socket ++ if (m_forwarder) ++ { ++ m_forwarder->reset(); ++ m_forwarder.reset(); ++ } ++ ++ // reset socket state ++ m_queue_size = 0; ++ m_mss = 1475; ++ m_cwnd = m_mss * 2; ++ m_bytes_in_flight = 0; ++ m_outstanding_packet_sizes.clear(); ++ m_recv_null_buffers = false; ++ m_send_null_buffers = false; ++ m_next_incoming_seq = 0; ++ m_next_outgoing_seq = 0; ++ m_last_drop_seq = 0; ++ ++ cancel(ec); ++ ++ ec.clear(); ++ } ++ catch (std::bad_alloc const&) ++ { ++ ec = make_error_code(boost::system::errc::not_enough_memory); ++ } ++ catch (boost::system::system_error const& err) ++ { ++ ec = err.code(); ++ } ++ ++ std::size_t tcp::socket::available(boost::system::error_code& ec) const ++ { ++ if (!m_open) ++ { ++ ec = boost::system::error_code(error::bad_descriptor); ++ return 0; ++ } ++ if (!m_channel) ++ { ++ ec = boost::system::error_code(error::not_connected); ++ return 0; ++ } ++ if (m_incoming_queue.empty()) ++ { ++ return 0; ++ } ++ ++ std::size_t ret = 0; ++ for (aux::packet const& p : m_incoming_queue) ++ { ++ if (p.type == aux::packet::type_t::error) ++ { ++ if (ret > 0) return ret; ++ ++ // if the read buffer is drained and there is an error, report that ++ // error. ++ ec = p.ec; ++ return 0; ++ } ++ ret += p.buffer.size(); ++ } ++ return ret; ++ } ++ ++ std::size_t tcp::socket::available() const ++ { ++ boost::system::error_code ec; ++ std::size_t ret = available(ec); ++ if (ec) throw boost::system::system_error(ec); ++ return ret; ++ } ++ ++ void tcp::socket::cancel(boost::system::error_code&) ++ { ++ abort_recv_handlers(); ++ abort_send_handlers(); ++ ++ if (m_connect_handler) ++ { ++ post(m_io_service, aux::make_malloc(std::bind(std::move(m_connect_handler) ++ , boost::system::error_code(error::operation_aborted)))); ++ m_connect_handler = nullptr; ++ } ++ } ++ ++ void tcp::socket::cancel() ++ { ++ boost::system::error_code ec; ++ cancel(ec); ++ if (ec) throw boost::system::system_error(ec); ++ } ++ ++ tcp::endpoint tcp::socket::remote_endpoint(boost::system::error_code& ec) const ++ { ++ if (!m_open) ++ { ++ ec = error::bad_descriptor; ++ return tcp::endpoint(); ++ } ++ ++ if (!m_channel) ++ { ++ ec = error::not_connected; ++ return tcp::endpoint(); ++ } ++ ++ int const remote = m_channel->remote_idx(m_bound_to); ++ return m_channel->visible_ep[remote]; ++ } ++ ++ tcp::endpoint tcp::socket::remote_endpoint() const ++ { ++ boost::system::error_code ec; ++ tcp::endpoint ret = remote_endpoint(ec); ++ if (ec) throw boost::system::system_error(ec); ++ return ret; ++ } ++ ++ void tcp::socket::async_connect(tcp::endpoint const& target ++ , aux::function h) ++ { ++ if (!m_open) open(target.protocol()); ++ ++ assert(h); ++ assert(!m_connect_handler); ++ ++ // find remote socket ++ boost::system::error_code ec; ++ if (m_bound_to.address() == ip::address()) ++ { ++ auto endpoint = ip::tcp::endpoint(); ++ if (target.address().is_v4()) { ++ endpoint.address(ip::address_v4::any()); ++ } else { ++ endpoint.address(ip::address_v6::any()); ++ } ++ ip::tcp::endpoint addr = m_io_service.bind_socket(this ++ , endpoint, ec); ++ if (ec) ++ { ++ post(m_io_service, aux::make_malloc(std::bind(std::move(h), ec))); ++ return; ++ } ++ m_bound_to = addr; ++ m_user_bound_to = addr; ++ } ++ if (m_bound_to.address().is_v4() != target.address().is_v4()) ++ { ++ post(m_io_service, aux::make_malloc(std::bind(std::move(h), ++ boost::system::error_code(error::address_family_not_supported)))); ++ return; ++ } ++ m_channel = m_io_service.internal_connect(this, target, ec); ++ m_mss = m_io_service.get_path_mtu(m_bound_to.address(), target.address()); ++ m_cwnd = m_mss * 2; ++ if (ec) ++ { ++ m_channel.reset(); ++ // TODO: ask the policy object what the round-trip to this endpoint is ++ m_connect_timer.expires_after(chrono::milliseconds(50)); ++ m_connect_timer.async_wait(aux::make_malloc(std::bind(std::move(h), ec))); ++ return; ++ } ++ ++ m_connect_handler = std::move(h); ++ ++ // the acceptor socket will call internal_connect_complete once the ++ // connection is established ++ } ++ ++ void tcp::socket::abort_recv_handlers() ++ { ++ if (m_recv_handler) post(m_io_service, aux::make_malloc(std::bind(std::move(m_recv_handler) ++ , boost::system::error_code(error::operation_aborted), std::size_t(0)))); ++ ++ if (m_wait_recv_handler) post(m_io_service, aux::make_malloc(std::bind(std::move(m_wait_recv_handler) ++ , boost::system::error_code(error::operation_aborted)))); ++ ++ m_recv_timer.cancel(); ++ m_recv_handler = nullptr; ++ m_wait_recv_handler = nullptr; ++ m_recv_buffer.clear(); ++ m_recv_null_buffers = false; ++ } ++ ++ void tcp::socket::abort_send_handlers() ++ { ++ if (m_send_handler) post(m_io_service, aux::make_malloc(std::bind(std::move(m_send_handler) ++ , boost::system::error_code(error::operation_aborted), std::size_t(0)))); ++ ++ if (m_wait_send_handler) post(m_io_service, aux::make_malloc(std::bind(std::move(m_wait_send_handler) ++ , boost::system::error_code(error::operation_aborted)))); ++ ++ m_send_handler = nullptr; ++ m_wait_send_handler = nullptr; ++ m_send_buffer.clear(); ++ m_send_null_buffers = false; ++ } ++ ++ void tcp::socket::async_write_some_impl(std::vector const& bufs ++ , aux::function handler) ++ { ++ boost::system::error_code ec; ++ std::size_t const bytes_transferred = write_some_impl(bufs, ec); ++ if (ec == boost::system::error_code(error::would_block)) ++ { ++ m_send_handler = std::move(handler); ++ m_send_buffer = bufs; ++ return; ++ } ++ ++ if (ec) ++ { ++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler), ec, std::size_t(0)))); ++ m_send_handler = nullptr; ++ m_send_buffer.clear(); ++ return; ++ } ++ ++ boost::system::error_code no_error; ++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler), no_error ++ , bytes_transferred))); ++ m_send_handler = nullptr; ++ m_send_buffer.clear(); ++ } ++ ++ std::size_t tcp::socket::write_some_impl( ++ std::vector const& bufs ++ , boost::system::error_code& ec) ++ { ++ if (!m_open) ++ { ++ ec = boost::system::error_code(error::bad_descriptor); ++ return 0; ++ } ++ if (!m_channel) ++ { ++ ec = boost::system::error_code(error::not_connected); ++ return 0; ++ } ++ ++ // the connect handler is used as proxy that this socket has not competed ++ // the connection yet. We're still waiting for SYN+ACK ++ if (m_connect_handler) ++ { ++ ec = boost::system::error_code(error::would_block); ++ return 0; ++ } ++ ++ int const remote = m_channel->remote_idx(m_bound_to); ++ route hops = m_channel->hops[remote]; ++ if (hops.empty()) ++ { ++ ec = boost::system::error_code(error::not_connected); ++ return 0; ++ } ++ ++ if (m_bytes_in_flight + m_mss > m_cwnd) ++ { ++ // this indicates that the send buffer is very large, we should ++ // probably not be able to stuff more bytes down it ++ // wait for the receiving end to pop some bytes off ++ ec = boost::system::error_code(error::would_block); ++ return 0; ++ } ++ ++ std::size_t ret = 0; ++ ++ for (auto const& buf : bufs) ++ { ++ // split up in packets ++ int buf_size = int(buf.size()); ++ std::uint8_t const* ptr = static_cast(buf.data()); ++ while (buf_size > 0) ++ { ++ int packet_size = (std::min)(buf_size, m_mss); ++ aux::packet p; ++ p.type = aux::packet::type_t::payload; ++ p.buffer.assign(ptr, ptr + packet_size); ++ p.from = asio::ip::udp::endpoint( ++ m_bound_to.address(), m_bound_to.port()); ++ p.overhead = 40; ++ p.hops = hops; ++ p.seq_nr = m_next_outgoing_seq++; ++ p.drop_fun = std::bind(&tcp::socket::packet_dropped, this, _1); ++ ++ send_packet(std::move(p)); ++ ptr += packet_size; ++ buf_size -= packet_size; ++ ret += packet_size; ++ ++ if (m_bytes_in_flight + m_mss > m_cwnd) ++ { ++ // the congestion window is full ++ if (ret == 0) ++ { ++ ec = boost::system::error_code(error::would_block); ++ return 0; ++ } ++ ++ return ret; ++ } ++ } ++ } ++ ++ return ret; ++ } ++ ++ std::size_t tcp::socket::read_some_impl( ++ std::vector const& bufs ++ , boost::system::error_code& ec) ++ { ++ // a zero-sized read always completes immediately with 0 bytes; ++ // boost.asio's SSL implementation relies on this to post a ++ // completion handler without transferring any data. ++ if (boost::asio::buffer_size(bufs) == 0) ++ { ++ ec.clear(); ++ return 0; ++ } ++ ++ assert(!bufs.empty()); ++ if (!m_open) ++ { ++ ec = boost::system::error_code(error::bad_descriptor); ++ return 0; ++ } ++ if (!m_channel) ++ { ++ ec = boost::system::error_code(error::not_connected); ++ return 0; ++ } ++ if (m_connect_handler) ++ { ++ // the socket is not done connecting yet ++ ec = boost::system::error_code(error::would_block); ++ return 0; ++ } ++ ++ if (m_incoming_queue.empty()) ++ { ++ ec = boost::system::error_code(error::would_block); ++ return 0; ++ } ++ ++ typedef std::vector buffers_t; ++ m_recv_buffer = bufs; ++ buffers_t::iterator recv_iter = m_recv_buffer.begin(); ++ int total_received = 0; ++ // the offset in the current receive buffer we're writing to. i.e. the ++ // buffer recv_iter points to ++ int buf_offset = 0; ++ ++ while (!m_incoming_queue.empty()) ++ { ++ aux::packet& p = m_incoming_queue.front(); ++ ++ if (p.type == aux::packet::type_t::error) ++ { ++ // if we have received bytes also, first deliver those. In the next ++ // read, deliver the error ++ if (total_received > 0) break; ++ ++ assert(p.ec); ++ ec = p.ec; ++ m_incoming_queue.erase(m_incoming_queue.begin()); ++ m_channel.reset(); ++ return 0; ++ } ++ else if (p.type == aux::packet::type_t::payload) ++ { ++ // copy bytes from the incoming queue into the receive buffer. ++ // both are vectors of buffer, so it can get a bit hairy ++ while (recv_iter != m_recv_buffer.end()) ++ { ++ int const buf_size = int(recv_iter->size()); ++ int const copy_size = (std::min)(int(p.buffer.size()) ++ , buf_size - buf_offset); ++ ++ memcpy(static_cast(recv_iter->data()) + buf_offset ++ , p.buffer.data(), copy_size); ++ ++ p.buffer.erase(p.buffer.begin(), p.buffer.begin() + copy_size); ++ m_queue_size -= copy_size; ++ ++ buf_offset += copy_size; ++ assert(buf_offset <= buf_size); ++ total_received += copy_size; ++ if (buf_offset == buf_size) ++ { ++ ++recv_iter; ++ buf_offset = 0; ++ } ++ ++ if (p.buffer.empty()) ++ { ++ m_incoming_queue.erase(m_incoming_queue.begin()); ++ break; ++ } ++ } ++ } ++ else ++ { ++ assert(false); ++ } ++ ++ if (recv_iter == m_recv_buffer.end()) ++ break; ++ } ++ ++ assert(total_received > 0); ++ ++ ec.clear(); ++ return total_received; ++ } ++ ++ void tcp::socket::async_read_some_impl(std::vector const& bufs ++ , aux::function handler) ++ { ++ // a zero-sized read always completes immediately with 0 bytes ++ // transferred, regardless of connection or queue state. boost.asio's ++ // SSL implementation relies on this to bounce a completion handler ++ // through the executor without actually transferring any data. ++ if (boost::asio::buffer_size(bufs) == 0) ++ { ++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler) ++ , boost::system::error_code(), std::size_t(0)))); ++ return; ++ } ++ ++ assert(!bufs.empty()); ++ ++ boost::system::error_code ec; ++ std::size_t bytes_transferred = read_some_impl(bufs, ec); ++ if (ec == boost::system::error_code(error::would_block)) ++ { ++ assert(m_incoming_queue.empty()); ++ ++ m_recv_buffer = bufs; ++ m_recv_handler = std::move(handler); ++ m_recv_null_buffers = false; ++ return; ++ } ++ ++ if (ec) ++ { ++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler), ec, std::size_t(0)))); ++ m_recv_handler = nullptr; ++ m_recv_buffer.clear(); ++ return; ++ } ++ ++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler), ec, bytes_transferred))); ++ m_recv_handler = nullptr; ++ m_recv_buffer.clear(); ++ } ++ ++ void tcp::socket::async_wait_read_impl( ++ aux::function handler) ++ { ++ boost::system::error_code ec; ++ // null_buffers notifies the handler when data is available, without ++ // reading any ++ int const bytes = int(available(ec)); ++ if (ec) ++ { ++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler), ec))); ++ m_recv_handler = nullptr; ++ m_recv_buffer.clear(); ++ return; ++ } ++ ++ if (bytes > 0) ++ { ++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler), ec))); ++ m_recv_handler = nullptr; ++ m_recv_buffer.clear(); ++ return; ++ } ++ ++ m_wait_recv_handler = std::move(handler); ++ m_recv_null_buffers = true; ++ } ++ ++ void tcp::socket::async_wait_write_impl( ++ aux::function handler) ++ { ++ if (!m_open) ++ { ++ boost::system::error_code const ec(error::bad_descriptor); ++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler), ec))); ++ return; ++ } ++ ++ if (!m_channel) ++ { ++ boost::system::error_code const ec(error::not_connected); ++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler), ec))); ++ return; ++ } ++ ++ if (!m_connect_handler && m_bytes_in_flight + m_mss <= m_cwnd) ++ { ++ // the socket is writable right now. complete immediately without ++ // actually writing anything, this is the null_buffers equivalent ++ // for writes ++ boost::system::error_code const ec; ++ post(m_io_service, aux::make_malloc(std::bind(std::move(handler), ec))); ++ return; ++ } ++ ++ m_wait_send_handler = std::move(handler); ++ m_send_null_buffers = true; ++ } ++ ++ // if there is an outstanding read operation, and this was the first incoming ++ // operation since we last drained, wake up the reader ++ void tcp::socket::maybe_wakeup_reader() ++ { ++ if (m_incoming_queue.size() != 1 || (!m_recv_handler && !m_wait_recv_handler)) return; ++ ++ if (m_recv_null_buffers) ++ { ++ async_wait_read_impl(std::move(m_wait_recv_handler)); ++ } ++ else ++ { ++ // we have an async. read operation outstanding, and we just put one ++ // packet in our incoming queue. ++ ++ // try to read from it and potentially fire the handler ++ async_read_some_impl(m_recv_buffer, std::move(m_recv_handler)); ++ } ++ } ++ ++ void tcp::socket::maybe_wakeup_writer() ++ { ++ if (!m_send_handler && !m_wait_send_handler) return; ++ ++ if (m_send_null_buffers) ++ { ++ async_wait_write_impl(std::move(m_wait_send_handler)); ++ } ++ else ++ { ++ // we have an async. write operation outstanding ++ async_write_some_impl(m_send_buffer, std::move(m_send_handler)); ++ } ++ } ++ ++ bool tcp::socket::internal_is_listening() { return false; } ++ ++ void tcp::socket::send_packet(aux::packet p) ++ { ++ m_bytes_in_flight += int(p.buffer.size()); ++ m_outstanding_packet_sizes[p.seq_nr] = int(p.buffer.size()); ++ ++ int const idx = m_channel->self_idx(m_bound_to); ++ p.byte_counter = m_channel->bytes_sent[idx]; ++ m_channel->bytes_sent[idx] += std::uint32_t(p.buffer.size()); ++ ++ auto* log = m_io_service.sim().get_pcap(); ++ if (log) ++ { ++ int const remote = m_channel->remote_idx(m_bound_to); ++ log->log_tcp(p, m_bound_to, m_channel->ep[remote]); ++ } ++ ++ forward_packet(std::move(p)); ++ } ++ ++ void tcp::socket::packet_dropped(aux::packet p) ++ { ++ int remote = m_channel->remote_idx(m_bound_to); ++ p.hops = m_channel->hops[remote]; ++ m_outgoing_packets.push_back(std::move(p)); ++ ++ const int packets_in_cwnd = m_cwnd / m_mss; ++ ++ // we just recently dropped a packet and cut the cwnd in half, ++ // don't do it again already ++ if (m_last_drop_seq > 0 && p.seq_nr < m_last_drop_seq + packets_in_cwnd) return; ++ ++ m_cwnd /= 2; ++ m_last_drop_seq = p.seq_nr; ++ ++ // TODO: this should really happen one second later to be accurate ++ if (m_cwnd < m_mss) m_cwnd = m_mss; ++ } ++ ++ void tcp::socket::incoming_packet(aux::packet p) ++ { ++ switch (p.type) ++ { ++ case aux::packet::type_t::uninitialized: ++ { ++ assert(false && "uninitialized packet"); ++ return; ++ } ++ case aux::packet::type_t::ack: ++ { ++ // if the socket just became writeable, we need to notify the ++ // client. First we want to know whether it was not writeable. ++ const bool was_writeable = m_bytes_in_flight + m_mss > m_cwnd; ++ ++ auto it = m_outstanding_packet_sizes.find(p.seq_nr); ++ assert(it != m_outstanding_packet_sizes.end()); ++ const int acked_bytes = it->second; ++ m_outstanding_packet_sizes.erase(it); ++ assert(m_bytes_in_flight >= acked_bytes); ++ m_bytes_in_flight -= acked_bytes; ++ ++ // potentially resend packets ++ while (!m_outgoing_packets.empty() ++ && m_bytes_in_flight ++ + int(m_outgoing_packets.front().buffer.size()) <= m_cwnd) ++ { ++ aux::packet pkt = std::move(m_outgoing_packets.front()); ++ m_outgoing_packets.erase(m_outgoing_packets.begin()); ++ send_packet(std::move(pkt)); ++ } ++ ++ // update cwnd based on the number of bytes ACKed. ++ // every round-trip, increase the window size by one packet ++ // (MSS) ++ m_cwnd += m_mss * acked_bytes / m_cwnd; ++ ++ // TODO: implement slow-start ++ ++ const bool is_writeable = m_bytes_in_flight + m_mss <= m_cwnd; ++ ++ if (!was_writeable && is_writeable) ++ maybe_wakeup_writer(); ++ ++ return; ++ } ++ case aux::packet::type_t::syn: ++ { ++ // TODO: return connection refused ++ return; ++ } ++ case aux::packet::type_t::syn_ack: ++ { ++ assert(m_connect_handler); ++ boost::system::error_code ec; ++ post(m_io_service, aux::make_malloc(std::bind(std::move(m_connect_handler), ec))); ++ m_connect_handler = nullptr; ++ if (ec) m_channel.reset(); ++ else maybe_wakeup_writer(); ++ return; ++ } ++ case aux::packet::type_t::error: ++ case aux::packet::type_t::payload: ++ { ++ aux::packet ack; ++ ack.type = aux::packet::type_t::ack; ++ ack.seq_nr = p.seq_nr; ++ ++ int remote = m_channel->remote_idx(m_bound_to); ++ ack.hops = m_channel->hops[remote]; ++ forward_packet(std::move(ack)); ++ ++ // if the sequence number is out-of-order, put it in the ++ // m_incoming_packets queue ++ if (p.seq_nr != m_next_incoming_seq) ++ { ++ if (p.seq_nr < m_next_incoming_seq) ++ { ++ std::printf("TCP: incoming sequence number lower (%" PRId64 ") " ++ "than expected: %" PRId64 "\n", p.seq_nr, m_next_incoming_seq); ++ } ++ ++ m_reorder_buffer.emplace(p.seq_nr, std::move(p)); ++ return; ++ } ++ ++ // this packet was in-order. increment the expected next sequence ++ // number. ++ ++m_next_incoming_seq; ++ m_incoming_queue.push_back(std::move(p)); ++ ++ // also, perhaps there are some packets that arrived out-of-order, ++ // check to see ++ auto it = m_reorder_buffer.find(m_next_incoming_seq); ++ while (it != m_reorder_buffer.end()) ++ { ++ aux::packet pkt = std::move(it->second); ++ m_reorder_buffer.erase(it); ++ m_incoming_queue.push_back(std::move(pkt)); ++ ++m_next_incoming_seq; ++ it = m_reorder_buffer.find(m_next_incoming_seq); ++ } ++ ++ maybe_wakeup_reader(); ++ return; ++ } ++ } ++ } ++} ++} ++} ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/src/udp_socket.cpp +@@ -0,0 +1,492 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#include "simulator/simulator.hpp" ++#include "simulator/packet.hpp" ++#include "simulator/pcap.hpp" ++#include "simulator/handler_allocator.hpp" ++ ++#include ++ ++#include "simulator/push_warnings.hpp" ++#include ++#include ++#include "simulator/pop_warnings.hpp" ++ ++typedef sim::chrono::high_resolution_clock::time_point time_point; ++typedef sim::chrono::high_resolution_clock::duration duration; ++ ++namespace sim { ++namespace asio { ++namespace ip { ++ ++ udp::socket::socket(io_context& ios) ++ : socket_base(ios) ++ , m_next_send(chrono::high_resolution_clock::now()) ++ , m_recv_sender(nullptr) ++ , m_recv_timer(ios) ++ , m_send_timer(ios) ++ , m_recv_null_buffers(0) ++ , m_queue_size(0) ++ , m_is_v4(true) ++ {} ++ ++ udp::socket::socket(socket&& s) ++ : socket_base(std::move(s)) ++ , m_next_send(std::move(s.m_next_send)) ++ , m_send_handler(std::move(s.m_send_handler)) ++ , m_wait_send_handler(std::move(s.m_wait_send_handler)) ++ , m_recv_handler(std::move(s.m_recv_handler)) ++ , m_wait_recv_handler(std::move(s.m_wait_recv_handler)) ++ , m_recv_buffer(std::move(s.m_recv_buffer)) ++ , m_recv_sender(std::move(s.m_recv_sender)) ++ , m_recv_timer(std::move(s.m_recv_timer)) ++ , m_send_timer(std::move(s.m_send_timer)) ++ , m_incoming_queue(std::move(s.m_incoming_queue)) ++ , m_recv_null_buffers(std::move(s.m_recv_null_buffers)) ++ , m_queue_size(std::move(s.m_queue_size)) ++ , m_is_v4(s.m_is_v4) ++ { ++ if (m_forwarder) m_forwarder->reset(this); ++ s.m_forwarder.reset(); ++ s.m_open = false; ++ s.m_bound_to = ip::udp::endpoint(); ++ if (m_bound_to != ip::udp::endpoint()) ++ m_io_service.rebind_udp_socket(this, m_bound_to); ++ } ++ ++ udp::socket::~socket() ++ { ++ boost::system::error_code ec; ++ close(ec); ++ } ++ ++ void udp::socket::bind(ip::udp::endpoint const& ep ++ , boost::system::error_code& ec) try ++ { ++ if (!m_open) ++ { ++ ec = error::bad_descriptor; ++ return; ++ } ++ ++ if (ep.address().is_v4() != m_is_v4) ++ { ++ ec = error::address_family_not_supported; ++ return; ++ } ++ ++ ip::udp::endpoint addr = m_io_service.bind_udp_socket(this, ep, ec); ++ if (ec) return; ++ m_bound_to = addr; ++ m_user_bound_to = addr; ++ } ++ catch (std::bad_alloc const&) ++ { ++ ec = make_error_code(boost::system::errc::not_enough_memory); ++ } ++ catch (boost::system::system_error const& err) ++ { ++ ec = err.code(); ++ } ++ ++ void udp::socket::bind(ip::udp::endpoint const& ep) ++ { ++ boost::system::error_code ec; ++ bind(ep, ec); ++ if (ec) throw boost::system::system_error(ec); ++ } ++ ++ void udp::socket::open(udp protocol ++ , boost::system::error_code& ec) try ++ { ++ // TODO: what if it's already open? ++ close(ec); ++ m_open = true; ++ m_is_v4 = (protocol == ip::udp::v4()); ++ m_forwarder = std::make_shared(this); ++ } ++ catch (std::bad_alloc const&) ++ { ++ ec = make_error_code(boost::system::errc::not_enough_memory); ++ } ++ catch (boost::system::system_error const& err) ++ { ++ ec = err.code(); ++ } ++ ++ void udp::socket::open(udp protocol) ++ { ++ boost::system::error_code ec; ++ open(protocol, ec); ++ if (ec) throw boost::system::system_error(ec); ++ } ++ ++ void udp::socket::close() ++ { ++ boost::system::error_code ec; ++ close(ec); ++ if (ec) throw boost::system::system_error(ec); ++ } ++ ++ void udp::socket::close(boost::system::error_code& ec) try ++ { ++ if (m_bound_to != ip::udp::endpoint()) ++ { ++ m_io_service.unbind_udp_socket(this, m_bound_to); ++ m_bound_to = ip::udp::endpoint(); ++ m_user_bound_to = ip::udp::endpoint(); ++ } ++ m_open = false; ++ ++ // prevent any more packets from being delivered to this socket ++ if (m_forwarder) ++ { ++ m_forwarder->reset(); ++ m_forwarder.reset(); ++ } ++ ++ cancel(ec); ++ } ++ catch (std::bad_alloc const&) ++ { ++ ec = make_error_code(boost::system::errc::not_enough_memory); ++ } ++ catch (boost::system::system_error const& err) ++ { ++ ec = err.code(); ++ } ++ ++ void udp::socket::cancel(boost::system::error_code&) ++ { ++ // cancel outstanding async operations ++ abort_recv_handlers(); ++ abort_send_handlers(); ++ m_recv_timer.cancel(); ++ m_send_timer.cancel(); ++ return; ++ } ++ ++ void udp::socket::cancel() ++ { ++ boost::system::error_code ec; ++ cancel(ec); ++ if (ec) throw boost::system::system_error(ec); ++ } ++ ++ void udp::socket::abort_send_handlers() ++ { ++ if (m_send_handler) ++ post(m_io_service, make_malloc(std::bind(std::ref(m_send_handler) ++ , boost::system::error_code(error::operation_aborted), std::size_t(0)))); ++ ++ if (m_wait_send_handler) ++ post(m_io_service, make_malloc(std::bind(std::ref(m_wait_send_handler) ++ , boost::system::error_code(error::operation_aborted)))); ++ ++ m_send_timer.cancel(); ++ m_send_handler = nullptr; ++ m_wait_send_handler = nullptr; ++// m_send_buffer.clear(); ++ } ++ ++ void udp::socket::abort_recv_handlers() ++ { ++ if (m_recv_handler) ++ post(m_io_service, make_malloc(std::bind(std::move(m_recv_handler) ++ , boost::system::error_code(error::operation_aborted), std::size_t(0)))); ++ ++ if (m_wait_recv_handler) ++ post(m_io_service, make_malloc(std::bind(std::move(m_wait_recv_handler) ++ , boost::system::error_code(error::operation_aborted)))); ++ ++ m_recv_timer.cancel(); ++ m_recv_handler = nullptr; ++ m_wait_recv_handler = nullptr; ++ m_recv_buffer.clear(); ++ } ++ ++ void udp::socket::async_wait(socket_base::wait_type_t const w ++ , aux::function handler) ++ { ++ if (w == wait_type_t::wait_write) ++ { ++ abort_send_handlers(); ++ ++ time_point const now = chrono::high_resolution_clock::now(); ++ boost::system::error_code no_error; ++ if (m_next_send - now > m_send_queue_time / 2) ++ { ++ // our send queue is too large. Defer ++ m_recv_timer.expires_at(m_next_send + m_send_queue_time / 2); ++ ++ m_wait_send_handler = std::move(handler); ++ m_recv_timer.async_wait(make_malloc(std::bind(std::ref(m_wait_send_handler), no_error))); ++ return; ++ } ++ ++ // the socket is writable, post the completion handler immediately ++ post(m_io_service, make_malloc(std::bind(std::move(handler), no_error))); ++ } ++ else if (w == wait_type_t::wait_read) ++ { ++ abort_recv_handlers(); ++ async_wait_receive_impl(nullptr, std::move(handler)); ++ } ++ } ++ ++ std::size_t udp::socket::receive_from_impl( ++ std::vector const& bufs ++ , udp::endpoint* sender ++ , socket_base::message_flags /* flags */ ++ , boost::system::error_code& ec) ++ { ++ assert(!bufs.empty()); ++ if (!m_open) ++ { ++ ec = boost::system::error_code(error::bad_descriptor); ++ return 0; ++ } ++ ++ if (m_bound_to == udp::endpoint()) ++ { ++ ec = boost::system::error_code(error::invalid_argument); ++ return 0; ++ } ++ ++ if (m_incoming_queue.empty()) ++ { ++ ec = boost::system::error_code(error::would_block); ++ return 0; ++ } ++ ++ aux::packet& p = m_incoming_queue.front(); ++ if (sender) *sender = p.from; ++ ++ int read = 0; ++ for (auto const& buf : bufs) ++ { ++ char* ptr = static_cast(buf.data()); ++ int const len = int(buf.size()); ++ int const to_copy = (std::min)(int(p.buffer.size()), len); ++ memcpy(ptr, p.buffer.data(), to_copy); ++ read += to_copy; ++ p.buffer.erase(p.buffer.begin(), p.buffer.begin() + to_copy); ++ m_queue_size -= to_copy; ++ if (p.buffer.empty()) break; ++ } ++ ++ m_incoming_queue.erase(m_incoming_queue.begin()); ++ return read; ++ } ++ ++ void udp::socket::async_wait_receive_impl( ++ udp::endpoint* sender ++ , aux::function handler) ++ { ++ if (!m_open) ++ { ++ post(m_io_service, make_malloc(std::bind(std::move(handler) ++ , boost::system::error_code(error::bad_descriptor)))); ++ return; ++ } ++ ++ if (m_bound_to == udp::endpoint()) ++ { ++ post(m_io_service, make_malloc(std::bind(std::move(handler) ++ , boost::system::error_code(error::invalid_argument)))); ++ return; ++ } ++ ++ if (!m_incoming_queue.empty()) ++ { ++ post(m_io_service, make_malloc(std::bind(std::move(handler), boost::system::error_code()))); ++ return; ++ } ++ ++ m_recv_null_buffers = true; ++ m_wait_recv_handler = std::move(handler); ++ m_recv_sender = sender; ++ } ++ ++ void udp::socket::async_receive_from_impl( ++ std::vector const& bufs ++ , udp::endpoint* sender ++ , socket_base::message_flags /* flags */ ++ , aux::function handler) ++ { ++ assert(!bufs.empty()); ++ ++ boost::system::error_code ec; ++ std::size_t bytes_transferred = receive_from_impl(bufs, sender, 0, ec); ++ if (ec == boost::system::error_code(error::would_block)) ++ { ++ m_recv_buffer = bufs; ++ m_recv_handler = std::move(handler); ++ m_recv_sender = sender; ++ m_recv_null_buffers = false; ++ ++ return; ++ } ++ ++ if (ec) ++ { ++ post(m_io_service, make_malloc(std::bind(std::move(handler), ec, std::size_t(0)))); ++ m_recv_handler = nullptr; ++ m_recv_buffer.clear(); ++ m_recv_sender = nullptr; ++ m_recv_null_buffers = false; ++ return; ++ } ++ ++ post(m_io_service, make_malloc(std::bind(std::move(handler), ec, bytes_transferred))); ++ m_recv_handler = nullptr; ++ m_recv_buffer.clear(); ++ m_recv_sender = nullptr; ++ m_recv_null_buffers = false; ++ } ++ ++ std::size_t udp::socket::send_to_impl(std::vector const& b ++ , udp::endpoint const& dst, message_flags /* flags */ ++ , boost::system::error_code& ec) ++ { ++ assert(m_non_blocking && "blocking operations not supported"); ++ ++ if (m_bound_to == ip::udp::endpoint()) ++ { ++ // the socket was not bound, bind to anything ++ bind(udp::endpoint(), ec); ++ if (ec) return 0; ++ } ++ ++ ec.clear(); ++ std::size_t ret = 0; ++ for (std::vector::const_iterator i = b.begin() ++ , end(b.end()); i != end; ++i) ++ { ++ ret += i->size(); ++ } ++ if (ret == 0) ++ { ++ ec = boost::system::error_code(error::invalid_argument); ++ return 0; ++ } ++ ++ time_point now = chrono::high_resolution_clock::now(); ++ ++ const int mtu = m_io_service.get_path_mtu(m_bound_to.address(), dst.address()); ++ ++ if (int(ret) > 65535) ++ { ++ ec = boost::system::error_code(error::message_size); ++ return 0; ++ } ++ ++ if (m_dont_fragment && int(ret) > mtu) ++ { ++ // silently drop packet ++ ec.clear(); ++ return ret; ++ } ++ ++ // determine the bandwidth in terms of nanoseconds / byte ++ const double nanoseconds_per_byte = 1000000000.0 ++ / double(aux::nic_bandwidth); ++ ++ if (m_next_send - now > m_send_queue_time) ++ { ++ // our send queue is too large. ++ ec = boost::system::error_code(asio::error::would_block); ++ return 0; ++ } ++ ++ route hops = m_io_service.find_udp_socket(*this, dst); ++ if (hops.empty()) ++ { ++ // the packet is silently dropped ++ // TODO: it would be nice if this would result in a round-trip time ++ // with an ICMP host unreachable or connection_refused error ++ return ret; ++ } ++ ++ hops.prepend(m_io_service.get_outgoing_route(m_bound_to.address())); ++ ++ m_next_send = std::max(now, m_next_send); ++ ++ aux::packet p; ++ p.overhead = 28; ++ p.type = aux::packet::type_t::payload; ++ p.from = m_bound_to; ++ p.hops = hops; ++ for (std::vector::const_iterator i = b.begin() ++ , end(b.end()); i != end; ++i) ++ { ++ p.buffer.insert(p.buffer.end(), static_cast(i->data()) ++ , static_cast(i->data()) + i->size()); ++ } ++ ++ auto* log = m_io_service.sim().get_pcap(); ++ if (log) log->log_udp(p, m_bound_to, dst); ++ ++ int const packet_size = int(p.buffer.size() + p.overhead); ++ forward_packet(std::move(p)); ++ ++ m_next_send += chrono::duration_cast(chrono::nanoseconds( ++ boost::int64_t(nanoseconds_per_byte * packet_size))); ++ ++ return ret; ++ } ++ ++ void udp::socket::incoming_packet(aux::packet p) ++ { ++ int const packet_size = int(p.buffer.size() + p.overhead); ++ ++ // silent drop. If the application isn't reading fast enough, drop packets ++ // TODO: make this limit controlled by SO_RECVBUF socket option ++ if (m_queue_size + packet_size > 256 * 1024) return; ++ ++ m_queue_size += int(p.buffer.size()); ++ m_incoming_queue.push_back(std::move(p)); ++ ++ maybe_wakeup_reader(); ++ } ++ ++ void udp::socket::maybe_wakeup_reader() ++ { ++ if (m_incoming_queue.size() != 1 || (!m_recv_handler && !m_wait_recv_handler)) return; ++ ++ // there is an outstanding operation waiting for an incoming packet ++ if (m_recv_null_buffers) ++ { ++ async_wait_receive_impl(m_recv_sender, std::move(m_wait_recv_handler)); ++ } ++ else ++ { ++ async_receive_from_impl(m_recv_buffer, m_recv_sender, 0, std::move(m_recv_handler)); ++ } ++ ++// m_recv_handler = nullptr; ++// m_recv_buffer.clear(); ++// m_recv_sender = nullptr; ++ } ++ ++} // ip ++} // asio ++} // sim ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/test/acceptor.cpp +@@ -0,0 +1,183 @@ ++/* ++ ++Copyright (c) 2015, Arvid Norberg ++All rights reserved. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ ++*/ ++ ++#include "simulator/simulator.hpp" ++#include ++ ++#include "catch.hpp" ++ ++#ifdef __GNUC__ ++// for CATCH's CHECK macro ++#pragma GCC diagnostic ignored "-Wparentheses" ++#endif ++ ++using namespace sim::asio; ++using namespace sim::chrono; ++using sim::simulation; ++using sim::default_config; ++using namespace std::placeholders; ++ ++namespace { ++char send_buffer[10000]; ++char recv_buffer[10000]; ++int num_received = 0; ++int num_sent = 0; ++ ++void on_sent(boost::system::error_code const& ec, std::size_t bytes_transferred ++ , ip::tcp::socket& sock) ++{ ++ int millis = int(duration_cast(high_resolution_clock::now() ++ .time_since_epoch()).count()); ++ if (ec) ++ { ++ std::printf("[%4d] send error %s\n", millis, ec.message().c_str()); ++ return; ++ } ++ ++ num_sent += int(bytes_transferred); ++ ++ std::printf("[%4d] sent %d bytes\n", millis, int(bytes_transferred)); ++ std::printf("closing\n"); ++ sock.close(); ++} ++ ++void on_receive(boost::system::error_code const& ec ++ , std::size_t bytes_transferred, ip::tcp::socket& sock) ++{ ++ int millis = int(duration_cast(high_resolution_clock::now() ++ .time_since_epoch()).count()); ++ if (ec) ++ { ++ std::printf("[%4d] receive error %s\n", millis, ec.message().c_str()); ++ return; ++ } ++ ++ num_received += int(bytes_transferred); ++ ++ std::printf("[%4d] received %d bytes\n", millis, int(bytes_transferred)); ++ ++ sock.async_read_some(sim::asio::buffer(recv_buffer, sizeof(recv_buffer)) ++ , std::bind(&on_receive, _1, _2, std::ref(sock))); ++} ++ ++void incoming_connection(boost::system::error_code const& ec ++ , ip::tcp::socket& sock, ip::tcp::endpoint const& ep) ++{ ++ int millis = int(duration_cast(high_resolution_clock::now() ++ .time_since_epoch()).count()); ++ if (ec) ++ { ++ std::printf("[%4d] error while accepting connection: %s\n" ++ , millis, ec.message().c_str()); ++ return; ++ } ++ ++ boost::system::error_code err; ++ ip::tcp::endpoint remote_endpoint = sock.remote_endpoint(err); ++ REQUIRE(!ec); ++ ip::tcp::endpoint local_endpoint = sock.local_endpoint(err); ++ REQUIRE(!ec); ++ std::printf("[%4d] received incoming connection from: %s:%d. local endpoint: %s:%d\n" ++ , millis, ep.address().to_string().c_str(), ep.port() ++ , local_endpoint.address().to_string().c_str(), local_endpoint.port()); ++ CHECK(local_endpoint.port() == 1337); ++ CHECK(local_endpoint.address().to_string() == "40.30.20.10"); ++ CHECK(remote_endpoint.port() != 0); ++ CHECK(remote_endpoint.address().to_string() == "10.20.30.40"); ++ ++ sock.async_read_some(sim::asio::buffer(recv_buffer, sizeof(recv_buffer)) ++ , std::bind(&on_receive, _1, _2, std::ref(sock))); ++} ++ ++void on_connected(boost::system::error_code const& ec ++ , ip::tcp::socket& sock) ++{ ++ int millis = int(duration_cast(high_resolution_clock::now() ++ .time_since_epoch()).count()); ++ if (ec) ++ { ++ std::printf("[%4d] error while connecting: %s\n", millis, ec.message().c_str()); ++ return; ++ } ++ ++ boost::system::error_code err; ++ ip::tcp::endpoint remote_endpoint = sock.remote_endpoint(err); ++ REQUIRE(!ec); ++ ip::tcp::endpoint local_endpoint = sock.local_endpoint(err); ++ REQUIRE(!ec); ++ ++ std::printf("[%4d] made outgoing connection to: %s:%d. local endpoint: %s:%d\n" ++ , millis ++ , remote_endpoint.address().to_string().c_str(), remote_endpoint.port() ++ , local_endpoint.address().to_string().c_str(), local_endpoint.port()); ++ ++ CHECK(remote_endpoint.port() == 1337); ++ CHECK(remote_endpoint.address().to_string() == "40.30.20.10"); ++ CHECK(local_endpoint.port() != 0); ++ CHECK(local_endpoint.address().to_string() == "10.20.30.40"); ++ ++ std::printf("sending %d bytes\n", int(sizeof(send_buffer))); ++ sock.async_write_some(sim::asio::buffer(send_buffer, sizeof(send_buffer)) ++ , std::bind(&on_sent, _1, _2, std::ref(sock))); ++} ++ ++} ++ ++TEST_CASE("accept incoming connection on acceptor socket", "[acceptor]") ++{ ++ default_config cfg; ++ simulation sim(cfg); ++ io_context incoming_ios(sim, ip::make_address_v4("40.30.20.10")); ++ io_context outgoing_ios(sim, ip::make_address_v4("10.20.30.40")); ++ ip::tcp::acceptor listener(incoming_ios); ++ ++ int millis = int(duration_cast(high_resolution_clock::now() ++ .time_since_epoch()).count()); ++ ++ boost::system::error_code ec; ++ listener.open(ip::tcp::v4(), ec); ++ REQUIRE(!ec); ++ listener.bind(ip::tcp::endpoint(ip::address(), 1337), ec); ++ REQUIRE(!ec); ++ listener.listen(10, ec); ++ REQUIRE(!ec); ++ ++ ip::tcp::socket incoming(incoming_ios); ++ ip::tcp::endpoint remote_endpoint; ++ listener.async_accept(incoming, remote_endpoint ++ , std::bind(&incoming_connection, _1, std::ref(incoming) ++ , std::cref(remote_endpoint))); ++ ++ dump_network_graph(sim, "accept.dot"); ++ ++ std::printf("[%4d] connecting\n", millis); ++ ip::tcp::socket outgoing(outgoing_ios); ++ outgoing.open(ip::tcp::v4(), ec); ++ REQUIRE(!ec); ++ outgoing.async_connect(ip::tcp::endpoint(ip::make_address("40.30.20.10") ++ , 1337), std::bind(&on_connected, _1, std::ref(outgoing))); ++ ++ sim.run(); ++ ++ millis = int(duration_cast(high_resolution_clock::now() ++ .time_since_epoch()).count()); ++ ++ CHECK(num_received == num_sent); ++ CHECK(num_received > 0); ++} ++ +--- /dev/null ++++ libtorrent-rasterbar-2.0.14/simulation/libsimulator/test/catch.hpp +@@ -0,0 +1,14057 @@ ++ ++// Copyright Catch2 Authors ++// Distributed under the Boost Software License, Version 1.0. ++// (See accompanying file LICENSE.txt or copy at ++// https://www.boost.org/LICENSE_1_0.txt) ++ ++// SPDX-License-Identifier: BSL-1.0 ++ ++// Catch v3.6.0 ++// Generated: 2024-05-05 20:53:27.071502 ++// ---------------------------------------------------------- ++// This file is an amalgamation of multiple different files. ++// You probably shouldn't edit it directly. ++// ---------------------------------------------------------- ++#ifndef CATCH_AMALGAMATED_HPP_INCLUDED ++#define CATCH_AMALGAMATED_HPP_INCLUDED ++ ++ ++/** \file ++ * This is a convenience header for Catch2. It includes **all** of Catch2 headers. ++ * ++ * Generally the Catch2 users should use specific includes they need, ++ * but this header can be used instead for ease-of-experimentation, or ++ * just plain convenience, at the cost of (significantly) increased ++ * compilation times. ++ * ++ * When a new header is added to either the top level folder, or to the ++ * corresponding internal subfolder, it should be added here. Headers ++ * added to the various subparts (e.g. matchers, generators, etc...), ++ * should go their respective catch-all headers. ++ */ ++ ++#ifndef CATCH_ALL_HPP_INCLUDED ++#define CATCH_ALL_HPP_INCLUDED ++ ++ ++ ++/** \file ++ * This is a convenience header for Catch2's benchmarking. It includes ++ * **all** of Catch2 headers related to benchmarking. ++ * ++ * Generally the Catch2 users should use specific includes they need, ++ * but this header can be used instead for ease-of-experimentation, or ++ * just plain convenience, at the cost of (significantly) increased ++ * compilation times. ++ * ++ * When a new header is added to either the `benchmark` folder, or to ++ * the corresponding internal (detail) subfolder, it should be added here. ++ */ ++ ++#ifndef CATCH_BENCHMARK_ALL_HPP_INCLUDED ++#define CATCH_BENCHMARK_ALL_HPP_INCLUDED ++ ++ ++ ++// Adapted from donated nonius code. ++ ++#ifndef CATCH_BENCHMARK_HPP_INCLUDED ++#define CATCH_BENCHMARK_HPP_INCLUDED ++ ++ ++ ++#ifndef CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED ++#define CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED ++ ++// Detect a number of compiler features - by compiler ++// The following features are defined: ++// ++// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? ++// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? ++// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? ++// **************** ++// Note to maintainers: if new toggles are added please document them ++// in configuration.md, too ++// **************** ++ ++// In general each macro has a _NO_ form ++// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. ++// Many features, at point of detection, define an _INTERNAL_ macro, so they ++// can be combined, en-mass, with the _NO_ forms later. ++ ++ ++ ++#ifndef CATCH_PLATFORM_HPP_INCLUDED ++#define CATCH_PLATFORM_HPP_INCLUDED ++ ++// See e.g.: ++// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html ++#ifdef __APPLE__ ++# ifndef __has_extension ++# define __has_extension(x) 0 ++# endif ++# include ++# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ ++ (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) ++# define CATCH_PLATFORM_MAC ++# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) ++# define CATCH_PLATFORM_IPHONE ++# endif ++ ++#elif defined(linux) || defined(__linux) || defined(__linux__) ++# define CATCH_PLATFORM_LINUX ++ ++#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) ++# define CATCH_PLATFORM_WINDOWS ++ ++# if defined( WINAPI_FAMILY ) && ( WINAPI_FAMILY == WINAPI_FAMILY_APP ) ++# define CATCH_PLATFORM_WINDOWS_UWP ++# endif ++ ++#elif defined(__ORBIS__) || defined(__PROSPERO__) ++# define CATCH_PLATFORM_PLAYSTATION ++ ++#endif ++ ++#endif // CATCH_PLATFORM_HPP_INCLUDED ++ ++#ifdef __cplusplus ++ ++# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) ++# define CATCH_CPP17_OR_GREATER ++# endif ++ ++# if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) ++# define CATCH_CPP20_OR_GREATER ++# endif ++ ++#endif ++ ++// Only GCC compiler should be used in this block, so other compilers trying to ++// mask themselves as GCC should be ignored. ++#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) && !defined(__NVCOMPILER) ++# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) ++# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) ++ ++// This only works on GCC 9+. so we have to also add a global suppression of Wparentheses ++// for older versions of GCC. ++# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ ++ _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) ++ ++# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ ++ _Pragma( "GCC diagnostic ignored \"-Wunused-result\"" ) ++ ++# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ ++ _Pragma( "GCC diagnostic ignored \"-Wunused-variable\"" ) ++ ++# define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ ++ _Pragma( "GCC diagnostic ignored \"-Wuseless-cast\"" ) ++ ++# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ ++ _Pragma( "GCC diagnostic ignored \"-Wshadow\"" ) ++ ++# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) ++ ++#endif ++ ++#if defined(__NVCOMPILER) ++# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "diag push" ) ++# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "diag pop" ) ++# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "diag_suppress declared_but_not_referenced" ) ++#endif ++ ++#if defined(__CUDACC__) && !defined(__clang__) ++# ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ ++// New pragmas introduced in CUDA 11.5+ ++# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "nv_diagnostic push" ) ++# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "nv_diagnostic pop" ) ++# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "nv_diag_suppress 177" ) ++# else ++# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "diag_suppress 177" ) ++# endif ++#endif ++ ++// clang-cl defines _MSC_VER as well as __clang__, which could cause the ++// start/stop internal suppression macros to be double defined. ++#if defined(__clang__) && !defined(_MSC_VER) ++ ++# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) ++# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) ++ ++#endif // __clang__ && !_MSC_VER ++ ++#if defined(__clang__) ++ ++// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug ++// which results in calls to destructors being emitted for each temporary, ++// without a matching initialization. In practice, this can result in something ++// like `std::string::~string` being called on an uninitialized value. ++// ++// For example, this code will likely segfault under IBM XL: ++// ``` ++// REQUIRE(std::string("12") + "34" == "1234") ++// ``` ++// ++// Similarly, NVHPC's implementation of `__builtin_constant_p` has a bug which ++// results in calls to the immediately evaluated lambda expressions to be ++// reported as unevaluated lambdas. ++// https://developer.nvidia.com/nvidia_bug/3321845. ++// ++// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. ++# if !defined(__ibmxl__) && !defined(__CUDACC__) && !defined( __NVCOMPILER ) ++# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ ++# endif ++ ++ ++# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ ++ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ ++ _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") ++ ++# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ ++ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) ++ ++# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ ++ _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) ++ ++# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ ++ _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) ++ ++# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ ++ _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) ++ ++# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \ ++ _Pragma( "clang diagnostic ignored \"-Wcomma\"" ) ++ ++# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ ++ _Pragma( "clang diagnostic ignored \"-Wshadow\"" ) ++ ++#endif // __clang__ ++ ++ ++//////////////////////////////////////////////////////////////////////////////// ++// We know some environments not to support full POSIX signals ++#if defined( CATCH_PLATFORM_WINDOWS ) || \ ++ defined( CATCH_PLATFORM_PLAYSTATION ) || \ ++ defined( __CYGWIN__ ) || \ ++ defined( __QNX__ ) || \ ++ defined( __EMSCRIPTEN__ ) || \ ++ defined( __DJGPP__ ) || \ ++ defined( __OS400__ ) ++# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS ++#else ++# define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS ++#endif ++ ++//////////////////////////////////////////////////////////////////////////////// ++// Assume that some platforms do not support getenv. ++#if defined( CATCH_PLATFORM_WINDOWS_UWP ) || \ ++ defined( CATCH_PLATFORM_PLAYSTATION ) || \ ++ defined( _GAMING_XBOX ) ++# define CATCH_INTERNAL_CONFIG_NO_GETENV ++#else ++# define CATCH_INTERNAL_CONFIG_GETENV ++#endif ++ ++//////////////////////////////////////////////////////////////////////////////// ++// Android somehow still does not support std::to_string ++#if defined(__ANDROID__) ++# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING ++#endif ++ ++//////////////////////////////////////////////////////////////////////////////// ++// Not all Windows environments support SEH properly ++#if defined(__MINGW32__) ++# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH ++#endif ++ ++//////////////////////////////////////////////////////////////////////////////// ++// PS4 ++#if defined(__ORBIS__) ++# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE ++#endif ++ ++//////////////////////////////////////////////////////////////////////////////// ++// Cygwin ++#ifdef __CYGWIN__ ++ ++// Required for some versions of Cygwin to declare gettimeofday ++// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin ++# define _BSD_SOURCE ++// some versions of cygwin (most) do not support std::to_string. Use the libstd check. ++// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 ++# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ ++ && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) ++ ++# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING ++ ++# endif ++#endif // __CYGWIN__ ++ ++//////////////////////////////////////////////////////////////////////////////// ++// Visual C++ ++#if defined(_MSC_VER) ++ ++// We want to defer to nvcc-specific warning suppression if we are compiled ++// with nvcc masquerading for MSVC. ++# if !defined( __CUDACC__ ) ++# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ ++ __pragma( warning( push ) ) ++# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ ++ __pragma( warning( pop ) ) ++# endif ++ ++// Universal Windows platform does not support SEH ++// Or console colours (or console at all...) ++# if defined(CATCH_PLATFORM_WINDOWS_UWP) ++# define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 ++# else ++# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH ++# endif ++ ++// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ ++// _MSVC_TRADITIONAL == 0 means new conformant preprocessor ++// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor ++# if !defined(__clang__) // Handle Clang masquerading for msvc ++# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) ++# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR ++# endif // MSVC_TRADITIONAL ++# endif // __clang__ ++ ++#endif // _MSC_VER ++ ++#if defined(_REENTRANT) || defined(_MSC_VER) ++// Enable async processing, as -pthread is specified or no additional linking is required ++# define CATCH_INTERNAL_CONFIG_USE_ASYNC ++#endif // _MSC_VER ++ ++//////////////////////////////////////////////////////////////////////////////// ++// Check if we are compiled with -fno-exceptions or equivalent ++#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) ++# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED ++#endif ++ ++ ++//////////////////////////////////////////////////////////////////////////////// ++// Embarcadero C++Build ++#if defined(__BORLANDC__) ++ #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN ++#endif ++ ++//////////////////////////////////////////////////////////////////////////////// ++ ++// RTX is a special version of Windows that is real time. ++// This means that it is detected as Windows, but does not provide ++// the same set of capabilities as real Windows does. ++#if defined(UNDER_RTSS) || defined(RTX64_BUILD) ++ #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH ++ #define CATCH_INTERNAL_CONFIG_NO_ASYNC ++ #define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 ++#endif ++ ++#if !defined(_GLIBCXX_USE_C99_MATH_TR1) ++#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER ++#endif ++ ++// Various stdlib support checks that require __has_include ++#if defined(__has_include) ++ // Check if string_view is available and usable ++ #if __has_include() && defined(CATCH_CPP17_OR_GREATER) ++ # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW ++ #endif ++ ++ // Check if optional is available and usable ++ # if __has_include() && defined(CATCH_CPP17_OR_GREATER) ++ # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL ++ # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) ++ ++ // Check if byte is available and usable ++ # if __has_include() && defined(CATCH_CPP17_OR_GREATER) ++ # include ++ # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) ++ # define CATCH_INTERNAL_CONFIG_CPP17_BYTE ++ # endif ++ # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) ++ ++ // Check if variant is available and usable ++ # if __has_include() && defined(CATCH_CPP17_OR_GREATER) ++ # if defined(__clang__) && (__clang_major__ < 8) ++ // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 ++ // fix should be in clang 8, workaround in libstdc++ 8.2 ++ # include ++ # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) ++ # define CATCH_CONFIG_NO_CPP17_VARIANT ++ # else ++ # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT ++ # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) ++ # else ++ # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT ++ # endif // defined(__clang__) && (__clang_major__ < 8) ++ # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) ++#endif // defined(__has_include) ++ ++ ++#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) ++# define CATCH_CONFIG_WINDOWS_SEH ++#endif ++// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. ++#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) ++# define CATCH_CONFIG_POSIX_SIGNALS ++#endif ++ ++#if defined(CATCH_INTERNAL_CONFIG_GETENV) && !defined(CATCH_INTERNAL_CONFIG_NO_GETENV) && !defined(CATCH_CONFIG_NO_GETENV) && !defined(CATCH_CONFIG_GETENV) ++# define CATCH_CONFIG_GETENV ++#endif ++ ++#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) ++# define CATCH_CONFIG_CPP11_TO_STRING ++#endif ++ ++#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) ++# define CATCH_CONFIG_CPP17_OPTIONAL ++#endif ++ ++#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) ++# define CATCH_CONFIG_CPP17_STRING_VIEW ++#endif ++ ++#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) ++# define CATCH_CONFIG_CPP17_VARIANT ++#endif ++ ++#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) ++# define CATCH_CONFIG_CPP17_BYTE ++#endif ++ ++ ++#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) ++# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE ++#endif ++ ++#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) ++# define CATCH_CONFIG_NEW_CAPTURE ++#endif ++ ++#if !defined( CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED ) && \ ++ !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) && \ ++ !defined( CATCH_CONFIG_NO_DISABLE_EXCEPTIONS ) ++# define CATCH_CONFIG_DISABLE_EXCEPTIONS ++#endif ++ ++#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) ++# define CATCH_CONFIG_POLYFILL_ISNAN ++#endif ++ ++#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) ++# define CATCH_CONFIG_USE_ASYNC ++#endif ++ ++#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) ++# define CATCH_CONFIG_GLOBAL_NEXTAFTER ++#endif ++ ++ ++// Even if we do not think the compiler has that warning, we still have ++// to provide a macro that can be used by the code. ++#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) ++# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION ++#endif ++#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) ++# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION ++#endif ++#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) ++# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS ++#endif ++#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) ++# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS ++#endif ++#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT) ++# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT ++#endif ++#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS) ++# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS ++#endif ++#if !defined(CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS) ++# define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS ++#endif ++#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) ++# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS ++#endif ++#if !defined( CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS ) ++# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS ++#endif ++#if !defined( CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS ) ++# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS ++#endif ++#if !defined( CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS ) ++# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS ++#endif ++ ++ ++// The goal of this macro is to avoid evaluation of the arguments, but ++// still have the compiler warn on problems inside... ++#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) ++# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) ++#endif ++ ++#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) ++# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS ++#elif defined(__clang__) && (__clang_major__ < 5) ++# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS ++#endif ++ ++ ++#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) ++#define CATCH_TRY if ((true)) ++#define CATCH_CATCH_ALL if ((false)) ++#define CATCH_CATCH_ANON(type) if ((false)) ++#else ++#define CATCH_TRY try ++#define CATCH_CATCH_ALL catch (...) ++#define CATCH_CATCH_ANON(type) catch (type) ++#endif ++ ++#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) ++#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR ++#endif ++ ++#if defined( CATCH_PLATFORM_WINDOWS ) && \ ++ !defined( CATCH_CONFIG_COLOUR_WIN32 ) && \ ++ !defined( CATCH_CONFIG_NO_COLOUR_WIN32 ) && \ ++ !defined( CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 ) ++# define CATCH_CONFIG_COLOUR_WIN32 ++#endif ++ ++#if defined( CATCH_CONFIG_SHARED_LIBRARY ) && defined( _MSC_VER ) && \ ++ !defined( CATCH_CONFIG_STATIC ) ++# ifdef Catch2_EXPORTS ++# define CATCH_EXPORT //__declspec( dllexport ) // not needed ++# else ++# define CATCH_EXPORT __declspec( dllimport ) ++# endif ++#else ++# define CATCH_EXPORT ++#endif ++ ++#endif // CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED ++ ++ ++#ifndef CATCH_CONTEXT_HPP_INCLUDED ++#define CATCH_CONTEXT_HPP_INCLUDED ++ ++ ++namespace Catch { ++ ++ class IResultCapture; ++ class IConfig; ++ ++ class Context { ++ IConfig const* m_config = nullptr; ++ IResultCapture* m_resultCapture = nullptr; ++ ++ CATCH_EXPORT static Context* currentContext; ++ friend Context& getCurrentMutableContext(); ++ friend Context const& getCurrentContext(); ++ static void createContext(); ++ friend void cleanUpContext(); ++ ++ public: ++ IResultCapture* getResultCapture() const { return m_resultCapture; } ++ IConfig const* getConfig() const { return m_config; } ++ void setResultCapture( IResultCapture* resultCapture ); ++ void setConfig( IConfig const* config ); ++ }; ++ ++ Context& getCurrentMutableContext(); ++ ++ inline Context const& getCurrentContext() { ++ // We duplicate the logic from `getCurrentMutableContext` here, ++ // to avoid paying the call overhead in debug mode. ++ if ( !Context::currentContext ) { Context::createContext(); } ++ // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) ++ return *Context::currentContext; ++ } ++ ++ void cleanUpContext(); ++ ++ class SimplePcg32; ++ SimplePcg32& sharedRng(); ++} ++ ++#endif // CATCH_CONTEXT_HPP_INCLUDED ++ ++ ++#ifndef CATCH_MOVE_AND_FORWARD_HPP_INCLUDED ++#define CATCH_MOVE_AND_FORWARD_HPP_INCLUDED ++ ++#include ++ ++//! Replacement for std::move with better compile time performance ++#define CATCH_MOVE(...) static_cast&&>(__VA_ARGS__) ++ ++//! Replacement for std::forward with better compile time performance ++#define CATCH_FORWARD(...) static_cast(__VA_ARGS__) ++ ++#endif // CATCH_MOVE_AND_FORWARD_HPP_INCLUDED ++ ++ ++#ifndef CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED ++#define CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED ++ ++namespace Catch { ++ ++ //! Used to signal that an assertion macro failed ++ struct TestFailureException{}; ++ //! Used to signal that the remainder of a test should be skipped ++ struct TestSkipException {}; ++ ++ /** ++ * Outlines throwing of `TestFailureException` into a single TU ++ * ++ * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. ++ */ ++ [[noreturn]] void throw_test_failure_exception(); ++ ++ /** ++ * Outlines throwing of `TestSkipException` into a single TU ++ * ++ * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. ++ */ ++ [[noreturn]] void throw_test_skip_exception(); ++ ++} // namespace Catch ++ ++#endif // CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED ++ ++ ++#ifndef CATCH_UNIQUE_NAME_HPP_INCLUDED ++#define CATCH_UNIQUE_NAME_HPP_INCLUDED ++ ++ ++ ++ ++/** \file ++ * Wrapper for the CONFIG configuration option ++ * ++ * When generating internal unique names, there are two options. Either ++ * we mix in the current line number, or mix in an incrementing number. ++ * We prefer the latter, using `__COUNTER__`, but users might want to ++ * use the former. ++ */ ++ ++#ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED ++#define CATCH_CONFIG_COUNTER_HPP_INCLUDED ++ ++ ++#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) ++ #define CATCH_INTERNAL_CONFIG_COUNTER ++#endif ++ ++#if defined( CATCH_INTERNAL_CONFIG_COUNTER ) && \ ++ !defined( CATCH_CONFIG_NO_COUNTER ) && \ ++ !defined( CATCH_CONFIG_COUNTER ) ++# define CATCH_CONFIG_COUNTER ++#endif ++ ++ ++#endif // CATCH_CONFIG_COUNTER_HPP_INCLUDED ++#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line ++#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) ++#ifdef CATCH_CONFIG_COUNTER ++# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) ++#else ++# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) ++#endif ++ ++#endif // CATCH_UNIQUE_NAME_HPP_INCLUDED ++ ++ ++#ifndef CATCH_INTERFACES_CAPTURE_HPP_INCLUDED ++#define CATCH_INTERFACES_CAPTURE_HPP_INCLUDED ++ ++#include ++#include ++ ++ ++ ++#ifndef CATCH_STRINGREF_HPP_INCLUDED ++#define CATCH_STRINGREF_HPP_INCLUDED ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++namespace Catch { ++ ++ /// A non-owning string class (similar to the forthcoming std::string_view) ++ /// Note that, because a StringRef may be a substring of another string, ++ /// it may not be null terminated. ++ class StringRef { ++ public: ++ using size_type = std::size_t; ++ using const_iterator = const char*; ++ ++ static constexpr size_type npos{ static_cast( -1 ) }; ++ ++ private: ++ static constexpr char const* const s_empty = ""; ++ ++ char const* m_start = s_empty; ++ size_type m_size = 0; ++ ++ public: // construction ++ constexpr StringRef() noexcept = default; ++ ++ StringRef( char const* rawChars ) noexcept; ++ ++ constexpr StringRef( char const* rawChars, size_type size ) noexcept ++ : m_start( rawChars ), ++ m_size( size ) ++ {} ++ ++ StringRef( std::string const& stdString ) noexcept ++ : m_start( stdString.c_str() ), ++ m_size( stdString.size() ) ++ {} ++ ++ explicit operator std::string() const { ++ return std::string(m_start, m_size); ++ } ++ ++ public: // operators ++ auto operator == ( StringRef other ) const noexcept -> bool { ++ return m_size == other.m_size ++ && (std::memcmp( m_start, other.m_start, m_size ) == 0); ++ } ++ auto operator != (StringRef other) const noexcept -> bool { ++ return !(*this == other); ++ } ++ ++ constexpr auto operator[] ( size_type index ) const noexcept -> char { ++ assert(index < m_size); ++ return m_start[index]; ++ } ++ ++ bool operator<(StringRef rhs) const noexcept; ++ ++ public: // named queries ++ constexpr auto empty() const noexcept -> bool { ++ return m_size == 0; ++ } ++ constexpr auto size() const noexcept -> size_type { ++ return m_size; ++ } ++ ++ // Returns a substring of [start, start + length). ++ // If start + length > size(), then the substring is [start, size()). ++ // If start > size(), then the substring is empty. ++ constexpr StringRef substr(size_type start, size_type length) const noexcept { ++ if (start < m_size) { ++ const auto shortened_size = m_size - start; ++ return StringRef(m_start + start, (shortened_size < length) ? shortened_size : length); ++ } else { ++ return StringRef(); ++ } ++ } ++ ++ // Returns the current start pointer. May not be null-terminated. ++ constexpr char const* data() const noexcept { ++ return m_start; ++ } ++ ++ constexpr const_iterator begin() const { return m_start; } ++ constexpr const_iterator end() const { return m_start + m_size; } ++ ++ ++ friend std::string& operator += (std::string& lhs, StringRef rhs); ++ friend std::ostream& operator << (std::ostream& os, StringRef str); ++ friend std::string operator+(StringRef lhs, StringRef rhs); ++ ++ /** ++ * Provides a three-way comparison with rhs ++ * ++ * Returns negative number if lhs < rhs, 0 if lhs == rhs, and a positive ++ * number if lhs > rhs ++ */ ++ int compare( StringRef rhs ) const; ++ }; ++ ++ ++ constexpr auto operator ""_sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { ++ return StringRef( rawChars, size ); ++ } ++} // namespace Catch ++ ++constexpr auto operator ""_catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { ++ return Catch::StringRef( rawChars, size ); ++} ++ ++#endif // CATCH_STRINGREF_HPP_INCLUDED ++ ++ ++#ifndef CATCH_RESULT_TYPE_HPP_INCLUDED ++#define CATCH_RESULT_TYPE_HPP_INCLUDED ++ ++namespace Catch { ++ ++ // ResultWas::OfType enum ++ struct ResultWas { enum OfType { ++ Unknown = -1, ++ Ok = 0, ++ Info = 1, ++ Warning = 2, ++ // TODO: Should explicit skip be considered "not OK" (cf. isOk)? I.e., should it have the failure bit? ++ ExplicitSkip = 4, ++ ++ FailureBit = 0x10, ++ ++ ExpressionFailed = FailureBit | 1, ++ ExplicitFailure = FailureBit | 2, ++ ++ Exception = 0x100 | FailureBit, ++ ++ ThrewException = Exception | 1, ++ DidntThrowException = Exception | 2, ++ ++ FatalErrorCondition = 0x200 | FailureBit ++ ++ }; }; ++ ++ bool isOk( ResultWas::OfType resultType ); ++ bool isJustInfo( int flags ); ++ ++ ++ // ResultDisposition::Flags enum ++ struct ResultDisposition { enum Flags { ++ Normal = 0x01, ++ ++ ContinueOnFailure = 0x02, // Failures fail test, but execution continues ++ FalseTest = 0x04, // Prefix expression with ! ++ SuppressFail = 0x08 // Failures are reported but do not fail the test ++ }; }; ++ ++ ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); ++ ++ bool shouldContinueOnFailure( int flags ); ++ inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } ++ bool shouldSuppressFailure( int flags ); ++ ++} // end namespace Catch ++ ++#endif // CATCH_RESULT_TYPE_HPP_INCLUDED ++ ++ ++#ifndef CATCH_UNIQUE_PTR_HPP_INCLUDED ++#define CATCH_UNIQUE_PTR_HPP_INCLUDED ++ ++#include ++#include ++ ++ ++namespace Catch { ++namespace Detail { ++ /** ++ * A reimplementation of `std::unique_ptr` for improved compilation performance ++ * ++ * Does not support arrays nor custom deleters. ++ */ ++ template ++ class unique_ptr { ++ T* m_ptr; ++ public: ++ constexpr unique_ptr(std::nullptr_t = nullptr): ++ m_ptr{} ++ {} ++ explicit constexpr unique_ptr(T* ptr): ++ m_ptr(ptr) ++ {} ++ ++ template ::value>> ++ unique_ptr(unique_ptr&& from): ++ m_ptr(from.release()) ++ {} ++ ++ template ::value>> ++ unique_ptr& operator=(unique_ptr&& from) { ++ reset(from.release()); ++ ++ return *this; ++ } ++ ++ unique_ptr(unique_ptr const&) = delete; ++ unique_ptr& operator=(unique_ptr const&) = delete; ++ ++ unique_ptr(unique_ptr&& rhs) noexcept: ++ m_ptr(rhs.m_ptr) { ++ rhs.m_ptr = nullptr; ++ } ++ unique_ptr& operator=(unique_ptr&& rhs) noexcept { ++ reset(rhs.release()); ++ ++ return *this; ++ } ++ ++ ~unique_ptr() { ++ delete m_ptr; ++ } ++ ++ T& operator*() { ++ assert(m_ptr); ++ return *m_ptr; ++ } ++ T const& operator*() const { ++ assert(m_ptr); ++ return *m_ptr; ++ } ++ T* operator->() noexcept { ++ assert(m_ptr); ++ return m_ptr; ++ } ++ T const* operator->() const noexcept { ++ assert(m_ptr); ++ return m_ptr; ++ } ++ ++ T* get() { return m_ptr; } ++ T const* get() const { return m_ptr; } ++ ++ void reset(T* ptr = nullptr) { ++ delete m_ptr; ++ m_ptr = ptr; ++ } ++ ++ T* release() { ++ auto temp = m_ptr; ++ m_ptr = nullptr; ++ return temp; ++ } ++ ++ explicit operator bool() const { ++ return m_ptr; ++ } ++ ++ friend void swap(unique_ptr& lhs, unique_ptr& rhs) { ++ auto temp = lhs.m_ptr; ++ lhs.m_ptr = rhs.m_ptr; ++ rhs.m_ptr = temp; ++ } ++ }; ++ ++ //! Specialization to cause compile-time error for arrays ++ template ++ class unique_ptr; ++ ++ template ++ unique_ptr make_unique(Args&&... args) { ++ return unique_ptr(new T(CATCH_FORWARD(args)...)); ++ } ++ ++ ++} // end namespace Detail ++} // end namespace Catch ++ ++#endif // CATCH_UNIQUE_PTR_HPP_INCLUDED ++ ++ ++#ifndef CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED ++#define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED ++ ++ ++ ++// Adapted from donated nonius code. ++ ++#ifndef CATCH_CLOCK_HPP_INCLUDED ++#define CATCH_CLOCK_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ namespace Benchmark { ++ using IDuration = std::chrono::nanoseconds; ++ using FDuration = std::chrono::duration; ++ ++ template ++ using TimePoint = typename Clock::time_point; ++ ++ using default_clock = std::chrono::steady_clock; ++ } // namespace Benchmark ++} // namespace Catch ++ ++#endif // CATCH_CLOCK_HPP_INCLUDED ++ ++namespace Catch { ++ ++ // We cannot forward declare the type with default template argument ++ // multiple times, so it is split out into a separate header so that ++ // we can prevent multiple declarations in dependees ++ template ++ struct BenchmarkStats; ++ ++} // end namespace Catch ++ ++#endif // CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED ++ ++namespace Catch { ++ ++ class AssertionResult; ++ struct AssertionInfo; ++ struct SectionInfo; ++ struct SectionEndInfo; ++ struct MessageInfo; ++ struct MessageBuilder; ++ struct Counts; ++ struct AssertionReaction; ++ struct SourceLineInfo; ++ ++ class ITransientExpression; ++ class IGeneratorTracker; ++ ++ struct BenchmarkInfo; ++ ++ namespace Generators { ++ class GeneratorUntypedBase; ++ using GeneratorBasePtr = Catch::Detail::unique_ptr; ++ } ++ ++ ++ class IResultCapture { ++ public: ++ virtual ~IResultCapture(); ++ ++ virtual void notifyAssertionStarted( AssertionInfo const& info ) = 0; ++ virtual bool sectionStarted( StringRef sectionName, ++ SourceLineInfo const& sectionLineInfo, ++ Counts& assertions ) = 0; ++ virtual void sectionEnded( SectionEndInfo&& endInfo ) = 0; ++ virtual void sectionEndedEarly( SectionEndInfo&& endInfo ) = 0; ++ ++ virtual IGeneratorTracker* ++ acquireGeneratorTracker( StringRef generatorName, ++ SourceLineInfo const& lineInfo ) = 0; ++ virtual IGeneratorTracker* ++ createGeneratorTracker( StringRef generatorName, ++ SourceLineInfo lineInfo, ++ Generators::GeneratorBasePtr&& generator ) = 0; ++ ++ virtual void benchmarkPreparing( StringRef name ) = 0; ++ virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; ++ virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0; ++ virtual void benchmarkFailed( StringRef error ) = 0; ++ ++ virtual void pushScopedMessage( MessageInfo const& message ) = 0; ++ virtual void popScopedMessage( MessageInfo const& message ) = 0; ++ ++ virtual void emplaceUnscopedMessage( MessageBuilder&& builder ) = 0; ++ ++ virtual void handleFatalErrorCondition( StringRef message ) = 0; ++ ++ virtual void handleExpr ++ ( AssertionInfo const& info, ++ ITransientExpression const& expr, ++ AssertionReaction& reaction ) = 0; ++ virtual void handleMessage ++ ( AssertionInfo const& info, ++ ResultWas::OfType resultType, ++ StringRef message, ++ AssertionReaction& reaction ) = 0; ++ virtual void handleUnexpectedExceptionNotThrown ++ ( AssertionInfo const& info, ++ AssertionReaction& reaction ) = 0; ++ virtual void handleUnexpectedInflightException ++ ( AssertionInfo const& info, ++ std::string&& message, ++ AssertionReaction& reaction ) = 0; ++ virtual void handleIncomplete ++ ( AssertionInfo const& info ) = 0; ++ virtual void handleNonExpr ++ ( AssertionInfo const &info, ++ ResultWas::OfType resultType, ++ AssertionReaction &reaction ) = 0; ++ ++ ++ ++ virtual bool lastAssertionPassed() = 0; ++ virtual void assertionPassed() = 0; ++ ++ // Deprecated, do not use: ++ virtual std::string getCurrentTestName() const = 0; ++ virtual const AssertionResult* getLastResult() const = 0; ++ virtual void exceptionEarlyReported() = 0; ++ }; ++ ++ IResultCapture& getResultCapture(); ++} ++ ++#endif // CATCH_INTERFACES_CAPTURE_HPP_INCLUDED ++ ++ ++#ifndef CATCH_INTERFACES_CONFIG_HPP_INCLUDED ++#define CATCH_INTERFACES_CONFIG_HPP_INCLUDED ++ ++ ++ ++#ifndef CATCH_NONCOPYABLE_HPP_INCLUDED ++#define CATCH_NONCOPYABLE_HPP_INCLUDED ++ ++namespace Catch { ++ namespace Detail { ++ ++ //! Deriving classes become noncopyable and nonmovable ++ class NonCopyable { ++ NonCopyable( NonCopyable const& ) = delete; ++ NonCopyable( NonCopyable&& ) = delete; ++ NonCopyable& operator=( NonCopyable const& ) = delete; ++ NonCopyable& operator=( NonCopyable&& ) = delete; ++ ++ protected: ++ NonCopyable() noexcept = default; ++ }; ++ ++ } // namespace Detail ++} // namespace Catch ++ ++#endif // CATCH_NONCOPYABLE_HPP_INCLUDED ++ ++#include ++#include ++#include ++#include ++ ++namespace Catch { ++ ++ enum class Verbosity { ++ Quiet = 0, ++ Normal, ++ High ++ }; ++ ++ struct WarnAbout { enum What { ++ Nothing = 0x00, ++ //! A test case or leaf section did not run any assertions ++ NoAssertions = 0x01, ++ //! A command line test spec matched no test cases ++ UnmatchedTestSpec = 0x02, ++ }; }; ++ ++ enum class ShowDurations { ++ DefaultForReporter, ++ Always, ++ Never ++ }; ++ enum class TestRunOrder { ++ Declared, ++ LexicographicallySorted, ++ Randomized ++ }; ++ enum class ColourMode : std::uint8_t { ++ //! Let Catch2 pick implementation based on platform detection ++ PlatformDefault, ++ //! Use ANSI colour code escapes ++ ANSI, ++ //! Use Win32 console colour API ++ Win32, ++ //! Don't use any colour ++ None ++ }; ++ struct WaitForKeypress { enum When { ++ Never, ++ BeforeStart = 1, ++ BeforeExit = 2, ++ BeforeStartAndExit = BeforeStart | BeforeExit ++ }; }; ++ ++ class TestSpec; ++ class IStream; ++ ++ class IConfig : public Detail::NonCopyable { ++ public: ++ virtual ~IConfig(); ++ ++ virtual bool allowThrows() const = 0; ++ virtual StringRef name() const = 0; ++ virtual bool includeSuccessfulResults() const = 0; ++ virtual bool shouldDebugBreak() const = 0; ++ virtual bool warnAboutMissingAssertions() const = 0; ++ virtual bool warnAboutUnmatchedTestSpecs() const = 0; ++ virtual bool zeroTestsCountAsSuccess() const = 0; ++ virtual int abortAfter() const = 0; ++ virtual bool showInvisibles() const = 0; ++ virtual ShowDurations showDurations() const = 0; ++ virtual double minDuration() const = 0; ++ virtual TestSpec const& testSpec() const = 0; ++ virtual bool hasTestFilters() const = 0; ++ virtual std::vector const& getTestsOrTags() const = 0; ++ virtual TestRunOrder runOrder() const = 0; ++ virtual uint32_t rngSeed() const = 0; ++ virtual unsigned int shardCount() const = 0; ++ virtual unsigned int shardIndex() const = 0; ++ virtual ColourMode defaultColourMode() const = 0; ++ virtual std::vector const& getSectionsToRun() const = 0; ++ virtual Verbosity verbosity() const = 0; ++ ++ virtual bool skipBenchmarks() const = 0; ++ virtual bool benchmarkNoAnalysis() const = 0; ++ virtual unsigned int benchmarkSamples() const = 0; ++ virtual double benchmarkConfidenceInterval() const = 0; ++ virtual unsigned int benchmarkResamples() const = 0; ++ virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0; ++ }; ++} ++ ++#endif // CATCH_INTERFACES_CONFIG_HPP_INCLUDED ++ ++ ++#ifndef CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED ++#define CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED ++ ++ ++#include ++ ++namespace Catch { ++ ++ class TestCaseHandle; ++ struct TestCaseInfo; ++ class ITestCaseRegistry; ++ class IExceptionTranslatorRegistry; ++ class IExceptionTranslator; ++ class ReporterRegistry; ++ class IReporterFactory; ++ class ITagAliasRegistry; ++ class ITestInvoker; ++ class IMutableEnumValuesRegistry; ++ struct SourceLineInfo; ++ ++ class StartupExceptionRegistry; ++ class EventListenerFactory; ++ ++ using IReporterFactoryPtr = Detail::unique_ptr; ++ ++ class IRegistryHub { ++ public: ++ virtual ~IRegistryHub(); // = default ++ ++ virtual ReporterRegistry const& getReporterRegistry() const = 0; ++ virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; ++ virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; ++ virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; ++ ++ ++ virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; ++ }; ++ ++ class IMutableRegistryHub { ++ public: ++ virtual ~IMutableRegistryHub(); // = default ++ virtual void registerReporter( std::string const& name, IReporterFactoryPtr factory ) = 0; ++ virtual void registerListener( Detail::unique_ptr factory ) = 0; ++ virtual void registerTest(Detail::unique_ptr&& testInfo, Detail::unique_ptr&& invoker) = 0; ++ virtual void registerTranslator( Detail::unique_ptr&& translator ) = 0; ++ virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; ++ virtual void registerStartupException() noexcept = 0; ++ virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0; ++ }; ++ ++ IRegistryHub const& getRegistryHub(); ++ IMutableRegistryHub& getMutableRegistryHub(); ++ void cleanUp(); ++ std::string translateActiveException(); ++ ++} ++ ++#endif // CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED ++ ++ ++#ifndef CATCH_BENCHMARK_STATS_HPP_INCLUDED ++#define CATCH_BENCHMARK_STATS_HPP_INCLUDED ++ ++ ++ ++// Adapted from donated nonius code. ++ ++#ifndef CATCH_ESTIMATE_HPP_INCLUDED ++#define CATCH_ESTIMATE_HPP_INCLUDED ++ ++namespace Catch { ++ namespace Benchmark { ++ template ++ struct Estimate { ++ Type point; ++ Type lower_bound; ++ Type upper_bound; ++ double confidence_interval; ++ }; ++ } // namespace Benchmark ++} // namespace Catch ++ ++#endif // CATCH_ESTIMATE_HPP_INCLUDED ++ ++ ++// Adapted from donated nonius code. ++ ++#ifndef CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED ++#define CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED ++ ++namespace Catch { ++ namespace Benchmark { ++ struct OutlierClassification { ++ int samples_seen = 0; ++ int low_severe = 0; // more than 3 times IQR below Q1 ++ int low_mild = 0; // 1.5 to 3 times IQR below Q1 ++ int high_mild = 0; // 1.5 to 3 times IQR above Q3 ++ int high_severe = 0; // more than 3 times IQR above Q3 ++ ++ int total() const { ++ return low_severe + low_mild + high_mild + high_severe; ++ } ++ }; ++ } // namespace Benchmark ++} // namespace Catch ++ ++#endif // CATCH_OUTLIERS_CLASSIFICATION_HPP_INCLUDED ++// The fwd decl & default specialization needs to be seen by VS2017 before ++// BenchmarkStats itself, or VS2017 will report compilation error. ++ ++#include ++#include ++ ++namespace Catch { ++ ++ struct BenchmarkInfo { ++ std::string name; ++ double estimatedDuration; ++ int iterations; ++ unsigned int samples; ++ unsigned int resamples; ++ double clockResolution; ++ double clockCost; ++ }; ++ ++ // We need to keep template parameter for backwards compatibility, ++ // but we also do not want to use the template paraneter. ++ template ++ struct BenchmarkStats { ++ BenchmarkInfo info; ++ ++ std::vector samples; ++ Benchmark::Estimate mean; ++ Benchmark::Estimate standardDeviation; ++ Benchmark::OutlierClassification outliers; ++ double outlierVariance; ++ }; ++ ++ ++} // end namespace Catch ++ ++#endif // CATCH_BENCHMARK_STATS_HPP_INCLUDED ++ ++ ++// Adapted from donated nonius code. ++ ++#ifndef CATCH_ENVIRONMENT_HPP_INCLUDED ++#define CATCH_ENVIRONMENT_HPP_INCLUDED ++ ++ ++namespace Catch { ++ namespace Benchmark { ++ struct EnvironmentEstimate { ++ FDuration mean; ++ OutlierClassification outliers; ++ }; ++ struct Environment { ++ EnvironmentEstimate clock_resolution; ++ EnvironmentEstimate clock_cost; ++ }; ++ } // namespace Benchmark ++} // namespace Catch ++ ++#endif // CATCH_ENVIRONMENT_HPP_INCLUDED ++ ++ ++// Adapted from donated nonius code. ++ ++#ifndef CATCH_EXECUTION_PLAN_HPP_INCLUDED ++#define CATCH_EXECUTION_PLAN_HPP_INCLUDED ++ ++ ++ ++// Adapted from donated nonius code. ++ ++#ifndef CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED ++#define CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED ++ ++ ++ ++// Adapted from donated nonius code. ++ ++#ifndef CATCH_CHRONOMETER_HPP_INCLUDED ++#define CATCH_CHRONOMETER_HPP_INCLUDED ++ ++ ++ ++// Adapted from donated nonius code. ++ ++#ifndef CATCH_OPTIMIZER_HPP_INCLUDED ++#define CATCH_OPTIMIZER_HPP_INCLUDED ++ ++#if defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__) ++# include // atomic_thread_fence ++#endif ++ ++ ++#include ++ ++namespace Catch { ++ namespace Benchmark { ++#if defined(__GNUC__) || defined(__clang__) ++ template ++ inline void keep_memory(T* p) { ++ asm volatile("" : : "g"(p) : "memory"); ++ } ++ inline void keep_memory() { ++ asm volatile("" : : : "memory"); ++ } ++ ++ namespace Detail { ++ inline void optimizer_barrier() { keep_memory(); } ++ } // namespace Detail ++#elif defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__) ++ ++#if defined(_MSVC_VER) ++#pragma optimize("", off) ++#elif defined(__IAR_SYSTEMS_ICC__) ++// For IAR the pragma only affects the following function ++#pragma optimize=disable ++#endif ++ template ++ inline void keep_memory(T* p) { ++ // thanks @milleniumbug ++ *reinterpret_cast(p) = *reinterpret_cast(p); ++ } ++ // TODO equivalent keep_memory() ++#if defined(_MSVC_VER) ++#pragma optimize("", on) ++#endif ++ ++ namespace Detail { ++ inline void optimizer_barrier() { ++ std::atomic_thread_fence(std::memory_order_seq_cst); ++ } ++ } // namespace Detail ++ ++#endif ++ ++ template ++ inline void deoptimize_value(T&& x) { ++ keep_memory(&x); ++ } ++ ++ template ++ inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t::value> { ++ deoptimize_value(CATCH_FORWARD(fn) (CATCH_FORWARD(args)...)); ++ } ++ ++ template ++ inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t::value> { ++ CATCH_FORWARD((fn)) (CATCH_FORWARD(args)...); ++ } ++ } // namespace Benchmark ++} // namespace Catch ++ ++#endif // CATCH_OPTIMIZER_HPP_INCLUDED ++ ++ ++#ifndef CATCH_META_HPP_INCLUDED ++#define CATCH_META_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ template ++ struct true_given : std::true_type {}; ++ ++ struct is_callable_tester { ++ template ++ static true_given()(std::declval()...))> test(int); ++ template ++ static std::false_type test(...); ++ }; ++ ++ template ++ struct is_callable; ++ ++ template ++ struct is_callable : decltype(is_callable_tester::test(0)) {}; ++ ++ ++#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703 ++ // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is ++ // replaced with std::invoke_result here. ++ template ++ using FunctionReturnType = std::remove_reference_t>>; ++#else ++ template ++ using FunctionReturnType = std::remove_reference_t>>; ++#endif ++ ++} // namespace Catch ++ ++namespace mpl_{ ++ struct na; ++} ++ ++#endif // CATCH_META_HPP_INCLUDED ++ ++namespace Catch { ++ namespace Benchmark { ++ namespace Detail { ++ struct ChronometerConcept { ++ virtual void start() = 0; ++ virtual void finish() = 0; ++ virtual ~ChronometerConcept(); // = default; ++ ++ ChronometerConcept() = default; ++ ChronometerConcept(ChronometerConcept const&) = default; ++ ChronometerConcept& operator=(ChronometerConcept const&) = default; ++ }; ++ template ++ struct ChronometerModel final : public ChronometerConcept { ++ void start() override { started = Clock::now(); } ++ void finish() override { finished = Clock::now(); } ++ ++ IDuration elapsed() const { ++ return std::chrono::duration_cast( ++ finished - started ); ++ } ++ ++ TimePoint started; ++ TimePoint finished; ++ }; ++ } // namespace Detail ++ ++ struct Chronometer { ++ public: ++ template ++ void measure(Fun&& fun) { measure(CATCH_FORWARD(fun), is_callable()); } ++ ++ int runs() const { return repeats; } ++ ++ Chronometer(Detail::ChronometerConcept& meter, int repeats_) ++ : impl(&meter) ++ , repeats(repeats_) {} ++ ++ private: ++ template ++ void measure(Fun&& fun, std::false_type) { ++ measure([&fun](int) { return fun(); }, std::true_type()); ++ } ++ ++ template ++ void measure(Fun&& fun, std::true_type) { ++ Detail::optimizer_barrier(); ++ impl->start(); ++ for (int i = 0; i < repeats; ++i) invoke_deoptimized(fun, i); ++ impl->finish(); ++ Detail::optimizer_barrier(); ++ } ++ ++ Detail::ChronometerConcept* impl; ++ int repeats; ++ }; ++ } // namespace Benchmark ++} // namespace Catch ++ ++#endif // CATCH_CHRONOMETER_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ namespace Benchmark { ++ namespace Detail { ++ template ++ struct is_related ++ : std::is_same, std::decay_t> {}; ++ ++ /// We need to reinvent std::function because every piece of code that might add overhead ++ /// in a measurement context needs to have consistent performance characteristics so that we ++ /// can account for it in the measurement. ++ /// Implementations of std::function with optimizations that aren't always applicable, like ++ /// small buffer optimizations, are not uncommon. ++ /// This is effectively an implementation of std::function without any such optimizations; ++ /// it may be slow, but it is consistently slow. ++ struct BenchmarkFunction { ++ private: ++ struct callable { ++ virtual void call(Chronometer meter) const = 0; ++ virtual Catch::Detail::unique_ptr clone() const = 0; ++ virtual ~callable(); // = default; ++ ++ callable() = default; ++ callable(callable const&) = default; ++ callable& operator=(callable const&) = default; ++ }; ++ template ++ struct model : public callable { ++ model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {} ++ model(Fun const& fun_) : fun(fun_) {} ++ ++ Catch::Detail::unique_ptr clone() const override { ++ return Catch::Detail::make_unique>( *this ); ++ } ++ ++ void call(Chronometer meter) const override { ++ call(meter, is_callable()); ++ } ++ void call(Chronometer meter, std::true_type) const { ++ fun(meter); ++ } ++ void call(Chronometer meter, std::false_type) const { ++ meter.measure(fun); ++ } ++ ++ Fun fun; ++ }; ++ ++ struct do_nothing { void operator()() const {} }; ++ ++ template ++ BenchmarkFunction(model* c) : f(c) {} ++ ++ public: ++ BenchmarkFunction() ++ : f(new model{ {} }) {} ++ ++ template ::value, int> = 0> ++ BenchmarkFunction(Fun&& fun) ++ : f(new model>(CATCH_FORWARD(fun))) {} ++ ++ BenchmarkFunction( BenchmarkFunction&& that ) noexcept: ++ f( CATCH_MOVE( that.f ) ) {} ++ ++ BenchmarkFunction(BenchmarkFunction const& that) ++ : f(that.f->clone()) {} ++ ++ BenchmarkFunction& ++ operator=( BenchmarkFunction&& that ) noexcept { ++ f = CATCH_MOVE( that.f ); ++ return *this; ++ } ++ ++ BenchmarkFunction& operator=(BenchmarkFunction const& that) { ++ f = that.f->clone(); ++ return *this; ++ } ++ ++ void operator()(Chronometer meter) const { f->call(meter); } ++ ++ private: ++ Catch::Detail::unique_ptr f; ++ }; ++ } // namespace Detail ++ } // namespace Benchmark ++} // namespace Catch ++ ++#endif // CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED ++ ++ ++// Adapted from donated nonius code. ++ ++#ifndef CATCH_REPEAT_HPP_INCLUDED ++#define CATCH_REPEAT_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ namespace Benchmark { ++ namespace Detail { ++ template ++ struct repeater { ++ void operator()(int k) const { ++ for (int i = 0; i < k; ++i) { ++ fun(); ++ } ++ } ++ Fun fun; ++ }; ++ template ++ repeater> repeat(Fun&& fun) { ++ return { CATCH_FORWARD(fun) }; ++ } ++ } // namespace Detail ++ } // namespace Benchmark ++} // namespace Catch ++ ++#endif // CATCH_REPEAT_HPP_INCLUDED ++ ++ ++// Adapted from donated nonius code. ++ ++#ifndef CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED ++#define CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED ++ ++ ++ ++// Adapted from donated nonius code. ++ ++#ifndef CATCH_MEASURE_HPP_INCLUDED ++#define CATCH_MEASURE_HPP_INCLUDED ++ ++ ++ ++// Adapted from donated nonius code. ++ ++#ifndef CATCH_COMPLETE_INVOKE_HPP_INCLUDED ++#define CATCH_COMPLETE_INVOKE_HPP_INCLUDED ++ ++ ++namespace Catch { ++ namespace Benchmark { ++ namespace Detail { ++ template ++ struct CompleteType { using type = T; }; ++ template <> ++ struct CompleteType { struct type {}; }; ++ ++ template ++ using CompleteType_t = typename CompleteType::type; ++ ++ template ++ struct CompleteInvoker { ++ template ++ static Result invoke(Fun&& fun, Args&&... args) { ++ return CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); ++ } ++ }; ++ template <> ++ struct CompleteInvoker { ++ template ++ static CompleteType_t invoke(Fun&& fun, Args&&... args) { ++ CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); ++ return {}; ++ } ++ }; ++ ++ // invoke and not return void :( ++ template ++ CompleteType_t> complete_invoke(Fun&& fun, Args&&... args) { ++ return CompleteInvoker>::invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...); ++ } ++ ++ } // namespace Detail ++ ++ template ++ Detail::CompleteType_t> user_code(Fun&& fun) { ++ return Detail::complete_invoke(CATCH_FORWARD(fun)); ++ } ++ } // namespace Benchmark ++} // namespace Catch ++ ++#endif // CATCH_COMPLETE_INVOKE_HPP_INCLUDED ++ ++ ++// Adapted from donated nonius code. ++ ++#ifndef CATCH_TIMING_HPP_INCLUDED ++#define CATCH_TIMING_HPP_INCLUDED ++ ++ ++#include ++ ++namespace Catch { ++ namespace Benchmark { ++ template ++ struct Timing { ++ IDuration elapsed; ++ Result result; ++ int iterations; ++ }; ++ template ++ using TimingOf = Timing>>; ++ } // namespace Benchmark ++} // namespace Catch ++ ++#endif // CATCH_TIMING_HPP_INCLUDED ++ ++namespace Catch { ++ namespace Benchmark { ++ namespace Detail { ++ template ++ TimingOf measure(Fun&& fun, Args&&... args) { ++ auto start = Clock::now(); ++ auto&& r = Detail::complete_invoke(fun, CATCH_FORWARD(args)...); ++ auto end = Clock::now(); ++ auto delta = end - start; ++ return { delta, CATCH_FORWARD(r), 1 }; ++ } ++ } // namespace Detail ++ } // namespace Benchmark ++} // namespace Catch ++ ++#endif // CATCH_MEASURE_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ namespace Benchmark { ++ namespace Detail { ++ template ++ TimingOf measure_one(Fun&& fun, int iters, std::false_type) { ++ return Detail::measure(fun, iters); ++ } ++ template ++ TimingOf measure_one(Fun&& fun, int iters, std::true_type) { ++ Detail::ChronometerModel meter; ++ auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters)); ++ ++ return { meter.elapsed(), CATCH_MOVE(result), iters }; ++ } ++ ++ template ++ using run_for_at_least_argument_t = std::conditional_t::value, Chronometer, int>; ++ ++ ++ [[noreturn]] ++ void throw_optimized_away_error(); ++ ++ template ++ TimingOf> ++ run_for_at_least(IDuration how_long, ++ const int initial_iterations, ++ Fun&& fun) { ++ auto iters = initial_iterations; ++ while (iters < (1 << 30)) { ++ auto&& Timing = measure_one(fun, iters, is_callable()); ++ ++ if (Timing.elapsed >= how_long) { ++ return { Timing.elapsed, CATCH_MOVE(Timing.result), iters }; ++ } ++ iters *= 2; ++ } ++ throw_optimized_away_error(); ++ } ++ } // namespace Detail ++ } // namespace Benchmark ++} // namespace Catch ++ ++#endif // CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ namespace Benchmark { ++ struct ExecutionPlan { ++ int iterations_per_sample; ++ FDuration estimated_duration; ++ Detail::BenchmarkFunction benchmark; ++ FDuration warmup_time; ++ int warmup_iterations; ++ ++ template ++ std::vector run(const IConfig &cfg, Environment env) const { ++ // warmup a bit ++ Detail::run_for_at_least( ++ std::chrono::duration_cast( warmup_time ), ++ warmup_iterations, ++ Detail::repeat( []() { return Clock::now(); } ) ++ ); ++ ++ std::vector times; ++ const auto num_samples = cfg.benchmarkSamples(); ++ times.reserve( num_samples ); ++ for ( size_t i = 0; i < num_samples; ++i ) { ++ Detail::ChronometerModel model; ++ this->benchmark( Chronometer( model, iterations_per_sample ) ); ++ auto sample_time = model.elapsed() - env.clock_cost.mean; ++ if ( sample_time < FDuration::zero() ) { ++ sample_time = FDuration::zero(); ++ } ++ times.push_back(sample_time / iterations_per_sample); ++ } ++ return times; ++ } ++ }; ++ } // namespace Benchmark ++} // namespace Catch ++ ++#endif // CATCH_EXECUTION_PLAN_HPP_INCLUDED ++ ++ ++// Adapted from donated nonius code. ++ ++#ifndef CATCH_ESTIMATE_CLOCK_HPP_INCLUDED ++#define CATCH_ESTIMATE_CLOCK_HPP_INCLUDED ++ ++ ++ ++// Adapted from donated nonius code. ++ ++#ifndef CATCH_STATS_HPP_INCLUDED ++#define CATCH_STATS_HPP_INCLUDED ++ ++ ++#include ++ ++namespace Catch { ++ namespace Benchmark { ++ namespace Detail { ++ using sample = std::vector; ++ ++ double weighted_average_quantile( int k, ++ int q, ++ double* first, ++ double* last ); ++ ++ OutlierClassification ++ classify_outliers( double const* first, double const* last ); ++ ++ double mean( double const* first, double const* last ); ++ ++ double normal_cdf( double x ); ++ ++ double erfc_inv(double x); ++ ++ double normal_quantile(double p); ++ ++ Estimate ++ bootstrap( double confidence_level, ++ double* first, ++ double* last, ++ sample const& resample, ++ double ( *estimator )( double const*, double const* ) ); ++ ++ struct bootstrap_analysis { ++ Estimate mean; ++ Estimate standard_deviation; ++ double outlier_variance; ++ }; ++ ++ bootstrap_analysis analyse_samples(double confidence_level, ++ unsigned int n_resamples, ++ double* first, ++ double* last); ++ } // namespace Detail ++ } // namespace Benchmark ++} // namespace Catch ++ ++#endif // CATCH_STATS_HPP_INCLUDED ++ ++#include ++#include ++#include ++ ++namespace Catch { ++ namespace Benchmark { ++ namespace Detail { ++ template ++ std::vector resolution(int k) { ++ std::vector> times; ++ times.reserve(static_cast(k + 1)); ++ for ( int i = 0; i < k + 1; ++i ) { ++ times.push_back( Clock::now() ); ++ } ++ ++ std::vector deltas; ++ deltas.reserve(static_cast(k)); ++ for ( size_t idx = 1; idx < times.size(); ++idx ) { ++ deltas.push_back( static_cast( ++ ( times[idx] - times[idx - 1] ).count() ) ); ++ } ++ ++ return deltas; ++ } ++ ++ constexpr auto warmup_iterations = 10000; ++ constexpr auto warmup_time = std::chrono::milliseconds(100); ++ constexpr auto minimum_ticks = 1000; ++ constexpr auto warmup_seed = 10000; ++ constexpr auto clock_resolution_estimation_time = std::chrono::milliseconds(500); ++ constexpr auto clock_cost_estimation_time_limit = std::chrono::seconds(1); ++ constexpr auto clock_cost_estimation_tick_limit = 100000; ++ constexpr auto clock_cost_estimation_time = std::chrono::milliseconds(10); ++ constexpr auto clock_cost_estimation_iterations = 10000; ++ ++ template ++ int warmup() { ++ return run_for_at_least(warmup_time, warmup_seed, &resolution) ++ .iterations; ++ } ++ template ++ EnvironmentEstimate estimate_clock_resolution(int iterations) { ++ auto r = run_for_at_least(clock_resolution_estimation_time, iterations, &resolution) ++ .result; ++ return { ++ FDuration(mean(r.data(), r.data() + r.size())), ++ classify_outliers(r.data(), r.data() + r.size()), ++ }; ++ } ++ template ++ EnvironmentEstimate estimate_clock_cost(FDuration resolution) { ++ auto time_limit = (std::min)( ++ resolution * clock_cost_estimation_tick_limit, ++ FDuration(clock_cost_estimation_time_limit)); ++ auto time_clock = [](int k) { ++ return Detail::measure([k] { ++ for (int i = 0; i < k; ++i) { ++ volatile auto ignored = Clock::now(); ++ (void)ignored; ++ } ++ }).elapsed; ++ }; ++ time_clock(1); ++ int iters = clock_cost_estimation_iterations; ++ auto&& r = run_for_at_least(clock_cost_estimation_time, iters, time_clock); ++ std::vector times; ++ int nsamples = static_cast(std::ceil(time_limit / r.elapsed)); ++ times.reserve(static_cast(nsamples)); ++ for ( int s = 0; s < nsamples; ++s ) { ++ times.push_back( static_cast( ++ ( time_clock( r.iterations ) / r.iterations ) ++ .count() ) ); ++ } ++ return { ++ FDuration(mean(times.data(), times.data() + times.size())), ++ classify_outliers(times.data(), times.data() + times.size()), ++ }; ++ } ++ ++ template ++ Environment measure_environment() { ++#if defined(__clang__) ++# pragma clang diagnostic push ++# pragma clang diagnostic ignored "-Wexit-time-destructors" ++#endif ++ static Catch::Detail::unique_ptr env; ++#if defined(__clang__) ++# pragma clang diagnostic pop ++#endif ++ if (env) { ++ return *env; ++ } ++ ++ auto iters = Detail::warmup(); ++ auto resolution = Detail::estimate_clock_resolution(iters); ++ auto cost = Detail::estimate_clock_cost(resolution.mean); ++ ++ env = Catch::Detail::make_unique( Environment{resolution, cost} ); ++ return *env; ++ } ++ } // namespace Detail ++ } // namespace Benchmark ++} // namespace Catch ++ ++#endif // CATCH_ESTIMATE_CLOCK_HPP_INCLUDED ++ ++ ++// Adapted from donated nonius code. ++ ++#ifndef CATCH_ANALYSE_HPP_INCLUDED ++#define CATCH_ANALYSE_HPP_INCLUDED ++ ++ ++ ++// Adapted from donated nonius code. ++ ++#ifndef CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED ++#define CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED ++ ++ ++#include ++ ++namespace Catch { ++ namespace Benchmark { ++ struct SampleAnalysis { ++ std::vector samples; ++ Estimate mean; ++ Estimate standard_deviation; ++ OutlierClassification outliers; ++ double outlier_variance; ++ }; ++ } // namespace Benchmark ++} // namespace Catch ++ ++#endif // CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED ++ ++ ++namespace Catch { ++ class IConfig; ++ ++ namespace Benchmark { ++ namespace Detail { ++ SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last); ++ } // namespace Detail ++ } // namespace Benchmark ++} // namespace Catch ++ ++#endif // CATCH_ANALYSE_HPP_INCLUDED ++ ++#include ++#include ++#include ++#include ++#include ++ ++namespace Catch { ++ namespace Benchmark { ++ struct Benchmark { ++ Benchmark(std::string&& benchmarkName) ++ : name(CATCH_MOVE(benchmarkName)) {} ++ ++ template ++ Benchmark(std::string&& benchmarkName , FUN &&func) ++ : fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {} ++ ++ template ++ ExecutionPlan prepare(const IConfig &cfg, Environment env) const { ++ auto min_time = env.clock_resolution.mean * Detail::minimum_ticks; ++ auto run_time = std::max(min_time, std::chrono::duration_cast(cfg.benchmarkWarmupTime())); ++ auto&& test = Detail::run_for_at_least(std::chrono::duration_cast(run_time), 1, fun); ++ int new_iters = static_cast(std::ceil(min_time * test.iterations / test.elapsed)); ++ return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast(cfg.benchmarkWarmupTime()), Detail::warmup_iterations }; ++ } ++ ++ template ++ void run() { ++ static_assert( Clock::is_steady, ++ "Benchmarking clock should be steady" ); ++ auto const* cfg = getCurrentContext().getConfig(); ++ ++ auto env = Detail::measure_environment(); ++ ++ getResultCapture().benchmarkPreparing(name); ++ CATCH_TRY{ ++ auto plan = user_code([&] { ++ return prepare(*cfg, env); ++ }); ++ ++ BenchmarkInfo info { ++ CATCH_MOVE(name), ++ plan.estimated_duration.count(), ++ plan.iterations_per_sample, ++ cfg->benchmarkSamples(), ++ cfg->benchmarkResamples(), ++ env.clock_resolution.mean.count(), ++ env.clock_cost.mean.count() ++ }; ++ ++ getResultCapture().benchmarkStarting(info); ++ ++ auto samples = user_code([&] { ++ return plan.template run(*cfg, env); ++ }); ++ ++ auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size()); ++ BenchmarkStats<> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; ++ getResultCapture().benchmarkEnded(stats); ++ } CATCH_CATCH_ANON (TestFailureException const&) { ++ getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr); ++ } CATCH_CATCH_ALL{ ++ getResultCapture().benchmarkFailed(translateActiveException()); ++ // We let the exception go further up so that the ++ // test case is marked as failed. ++ std::rethrow_exception(std::current_exception()); ++ } ++ } ++ ++ // sets lambda to be used in fun *and* executes benchmark! ++ template ::value, int> = 0> ++ Benchmark & operator=(Fun func) { ++ auto const* cfg = getCurrentContext().getConfig(); ++ if (!cfg->skipBenchmarks()) { ++ fun = Detail::BenchmarkFunction(func); ++ run(); ++ } ++ return *this; ++ } ++ ++ explicit operator bool() { ++ return true; ++ } ++ ++ private: ++ Detail::BenchmarkFunction fun; ++ std::string name; ++ }; ++ } ++} // namespace Catch ++ ++#define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1 ++#define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2 ++ ++#define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\ ++ if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ ++ BenchmarkName = [&](int benchmarkIndex) ++ ++#define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\ ++ if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ ++ BenchmarkName = [&] ++ ++#if defined(CATCH_CONFIG_PREFIX_ALL) ++ ++#define CATCH_BENCHMARK(...) \ ++ INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) ++#define CATCH_BENCHMARK_ADVANCED(name) \ ++ INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name) ++ ++#else ++ ++#define BENCHMARK(...) \ ++ INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) ++#define BENCHMARK_ADVANCED(name) \ ++ INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name) ++ ++#endif ++ ++#endif // CATCH_BENCHMARK_HPP_INCLUDED ++ ++ ++// Adapted from donated nonius code. ++ ++#ifndef CATCH_CONSTRUCTOR_HPP_INCLUDED ++#define CATCH_CONSTRUCTOR_HPP_INCLUDED ++ ++ ++#include ++ ++namespace Catch { ++ namespace Benchmark { ++ namespace Detail { ++ template ++ struct ObjectStorage ++ { ++ ObjectStorage() = default; ++ ++ ObjectStorage(const ObjectStorage& other) ++ { ++ new(&data) T(other.stored_object()); ++ } ++ ++ ObjectStorage(ObjectStorage&& other) ++ { ++ new(data) T(CATCH_MOVE(other.stored_object())); ++ } ++ ++ ~ObjectStorage() { destruct_on_exit(); } ++ ++ template ++ void construct(Args&&... args) ++ { ++ new (data) T(CATCH_FORWARD(args)...); ++ } ++ ++ template ++ std::enable_if_t destruct() ++ { ++ stored_object().~T(); ++ } ++ ++ private: ++ // If this is a constructor benchmark, destruct the underlying object ++ template ++ void destruct_on_exit(std::enable_if_t* = nullptr) { destruct(); } ++ // Otherwise, don't ++ template ++ void destruct_on_exit(std::enable_if_t* = nullptr) { } ++ ++#if defined( __GNUC__ ) && __GNUC__ <= 6 ++# pragma GCC diagnostic push ++# pragma GCC diagnostic ignored "-Wstrict-aliasing" ++#endif ++ T& stored_object() { return *reinterpret_cast( data ); } ++ ++ T const& stored_object() const { ++ return *reinterpret_cast( data ); ++ } ++#if defined( __GNUC__ ) && __GNUC__ <= 6 ++# pragma GCC diagnostic pop ++#endif ++ ++ alignas( T ) unsigned char data[sizeof( T )]{}; ++ }; ++ } // namespace Detail ++ ++ template ++ using storage_for = Detail::ObjectStorage; ++ ++ template ++ using destructable_object = Detail::ObjectStorage; ++ } // namespace Benchmark ++} // namespace Catch ++ ++#endif // CATCH_CONSTRUCTOR_HPP_INCLUDED ++ ++#endif // CATCH_BENCHMARK_ALL_HPP_INCLUDED ++ ++ ++#ifndef CATCH_APPROX_HPP_INCLUDED ++#define CATCH_APPROX_HPP_INCLUDED ++ ++ ++ ++#ifndef CATCH_TOSTRING_HPP_INCLUDED ++#define CATCH_TOSTRING_HPP_INCLUDED ++ ++ ++#include ++#include ++#include ++#include ++ ++ ++ ++ ++/** \file ++ * Wrapper for the WCHAR configuration option ++ * ++ * We want to support platforms that do not provide `wchar_t`, so we ++ * sometimes have to disable providing wchar_t overloads through Catch2, ++ * e.g. the StringMaker specialization for `std::wstring`. ++ */ ++ ++#ifndef CATCH_CONFIG_WCHAR_HPP_INCLUDED ++#define CATCH_CONFIG_WCHAR_HPP_INCLUDED ++ ++ ++// We assume that WCHAR should be enabled by default, and only disabled ++// for a shortlist (so far only DJGPP) of compilers. ++ ++#if defined(__DJGPP__) ++# define CATCH_INTERNAL_CONFIG_NO_WCHAR ++#endif // __DJGPP__ ++ ++#if !defined( CATCH_INTERNAL_CONFIG_NO_WCHAR ) && \ ++ !defined( CATCH_CONFIG_NO_WCHAR ) && \ ++ !defined( CATCH_CONFIG_WCHAR ) ++# define CATCH_CONFIG_WCHAR ++#endif ++ ++#endif // CATCH_CONFIG_WCHAR_HPP_INCLUDED ++ ++ ++#ifndef CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED ++#define CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED ++ ++ ++#include ++#include ++#include ++#include ++ ++namespace Catch { ++ ++ class ReusableStringStream : Detail::NonCopyable { ++ std::size_t m_index; ++ std::ostream* m_oss; ++ public: ++ ReusableStringStream(); ++ ~ReusableStringStream(); ++ ++ //! Returns the serialized state ++ std::string str() const; ++ //! Sets internal state to `str` ++ void str(std::string const& str); ++ ++#if defined(__GNUC__) && !defined(__clang__) ++#pragma GCC diagnostic push ++// Old versions of GCC do not understand -Wnonnull-compare ++#pragma GCC diagnostic ignored "-Wpragmas" ++// Streaming a function pointer triggers Waddress and Wnonnull-compare ++// on GCC, because it implicitly converts it to bool and then decides ++// that the check it uses (a? true : false) is tautological and cannot ++// be null... ++#pragma GCC diagnostic ignored "-Waddress" ++#pragma GCC diagnostic ignored "-Wnonnull-compare" ++#endif ++ ++ template ++ auto operator << ( T const& value ) -> ReusableStringStream& { ++ *m_oss << value; ++ return *this; ++ } ++ ++#if defined(__GNUC__) && !defined(__clang__) ++#pragma GCC diagnostic pop ++#endif ++ auto get() -> std::ostream& { return *m_oss; } ++ }; ++} ++ ++#endif // CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED ++ ++ ++#ifndef CATCH_VOID_TYPE_HPP_INCLUDED ++#define CATCH_VOID_TYPE_HPP_INCLUDED ++ ++ ++namespace Catch { ++ namespace Detail { ++ ++ template ++ struct make_void { using type = void; }; ++ ++ template ++ using void_t = typename make_void::type; ++ ++ } // namespace Detail ++} // namespace Catch ++ ++ ++#endif // CATCH_VOID_TYPE_HPP_INCLUDED ++ ++ ++#ifndef CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED ++#define CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED ++ ++ ++#include ++ ++namespace Catch { ++ ++ namespace Detail { ++ struct EnumInfo { ++ StringRef m_name; ++ std::vector> m_values; ++ ++ ~EnumInfo(); ++ ++ StringRef lookup( int value ) const; ++ }; ++ } // namespace Detail ++ ++ class IMutableEnumValuesRegistry { ++ public: ++ virtual ~IMutableEnumValuesRegistry(); // = default; ++ ++ virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector const& values ) = 0; ++ ++ template ++ Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list values ) { ++ static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int"); ++ std::vector intValues; ++ intValues.reserve( values.size() ); ++ for( auto enumValue : values ) ++ intValues.push_back( static_cast( enumValue ) ); ++ return registerEnum( enumName, allEnums, intValues ); ++ } ++ }; ++ ++} // Catch ++ ++#endif // CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED ++ ++#ifdef CATCH_CONFIG_CPP17_STRING_VIEW ++#include ++#endif ++ ++#ifdef _MSC_VER ++#pragma warning(push) ++#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless ++#endif ++ ++// We need a dummy global operator<< so we can bring it into Catch namespace later ++struct Catch_global_namespace_dummy{}; ++std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); ++ ++namespace Catch { ++ // Bring in global namespace operator<< for ADL lookup in ++ // `IsStreamInsertable` below. ++ using ::operator<<; ++ ++ namespace Detail { ++ ++ inline std::size_t catch_strnlen(const char *str, std::size_t n) { ++ auto ret = std::char_traits::find(str, n, '\0'); ++ if (ret != nullptr) { ++ return static_cast(ret - str); ++ } ++ return n; ++ } ++ ++ constexpr StringRef unprintableString = "{?}"_sr; ++ ++ //! Encases `string in quotes, and optionally escapes invisibles ++ std::string convertIntoString( StringRef string, bool escapeInvisibles ); ++ ++ //! Encases `string` in quotes, and escapes invisibles if user requested ++ //! it via CLI ++ std::string convertIntoString( StringRef string ); ++ ++ std::string rawMemoryToString( const void *object, std::size_t size ); ++ ++ template ++ std::string rawMemoryToString( const T& object ) { ++ return rawMemoryToString( &object, sizeof(object) ); ++ } ++ ++ template ++ class IsStreamInsertable { ++ template ++ static auto test(int) ++ -> decltype(std::declval() << std::declval(), std::true_type()); ++ ++ template ++ static auto test(...)->std::false_type; ++ ++ public: ++ static const bool value = decltype(test(0))::value; ++ }; ++ ++ template ++ std::string convertUnknownEnumToString( E e ); ++ ++ template ++ std::enable_if_t< ++ !std::is_enum::value && !std::is_base_of::value, ++ std::string> convertUnstreamable( T const& ) { ++ return std::string(Detail::unprintableString); ++ } ++ template ++ std::enable_if_t< ++ !std::is_enum::value && std::is_base_of::value, ++ std::string> convertUnstreamable(T const& ex) { ++ return ex.what(); ++ } ++ ++ ++ template ++ std::enable_if_t< ++ std::is_enum::value, ++ std::string> convertUnstreamable( T const& value ) { ++ return convertUnknownEnumToString( value ); ++ } ++ ++#if defined(_MANAGED) ++ //! Convert a CLR string to a utf8 std::string ++ template ++ std::string clrReferenceToString( T^ ref ) { ++ if (ref == nullptr) ++ return std::string("null"); ++ auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); ++ cli::pin_ptr p = &bytes[0]; ++ return std::string(reinterpret_cast(p), bytes->Length); ++ } ++#endif ++ ++ } // namespace Detail ++ ++ ++ template ++ struct StringMaker { ++ template ++ static ++ std::enable_if_t<::Catch::Detail::IsStreamInsertable::value, std::string> ++ convert(const Fake& value) { ++ ReusableStringStream rss; ++ // NB: call using the function-like syntax to avoid ambiguity with ++ // user-defined templated operator<< under clang. ++ rss.operator<<(value); ++ return rss.str(); ++ } ++ ++ template ++ static ++ std::enable_if_t::value, std::string> ++ convert( const Fake& value ) { ++#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER) ++ return Detail::convertUnstreamable(value); ++#else ++ return CATCH_CONFIG_FALLBACK_STRINGIFIER(value); ++#endif ++ } ++ }; ++ ++ namespace Detail { ++ ++ // This function dispatches all stringification requests inside of Catch. ++ // Should be preferably called fully qualified, like ::Catch::Detail::stringify ++ template ++ std::string stringify(const T& e) { ++ return ::Catch::StringMaker>>::convert(e); ++ } ++ ++ template ++ std::string convertUnknownEnumToString( E e ) { ++ return ::Catch::Detail::stringify(static_cast>(e)); ++ } ++ ++#if defined(_MANAGED) ++ template ++ std::string stringify( T^ e ) { ++ return ::Catch::StringMaker::convert(e); ++ } ++#endif ++ ++ } // namespace Detail ++ ++ // Some predefined specializations ++ ++ template<> ++ struct StringMaker { ++ static std::string convert(const std::string& str); ++ }; ++ ++#ifdef CATCH_CONFIG_CPP17_STRING_VIEW ++ template<> ++ struct StringMaker { ++ static std::string convert(std::string_view str); ++ }; ++#endif ++ ++ template<> ++ struct StringMaker { ++ static std::string convert(char const * str); ++ }; ++ template<> ++ struct StringMaker { ++ static std::string convert(char * str); ++ }; ++ ++#if defined(CATCH_CONFIG_WCHAR) ++ template<> ++ struct StringMaker { ++ static std::string convert(const std::wstring& wstr); ++ }; ++ ++# ifdef CATCH_CONFIG_CPP17_STRING_VIEW ++ template<> ++ struct StringMaker { ++ static std::string convert(std::wstring_view str); ++ }; ++# endif ++ ++ template<> ++ struct StringMaker { ++ static std::string convert(wchar_t const * str); ++ }; ++ template<> ++ struct StringMaker { ++ static std::string convert(wchar_t * str); ++ }; ++#endif // CATCH_CONFIG_WCHAR ++ ++ template ++ struct StringMaker { ++ static std::string convert(char const* str) { ++ return Detail::convertIntoString( ++ StringRef( str, Detail::catch_strnlen( str, SZ ) ) ); ++ } ++ }; ++ template ++ struct StringMaker { ++ static std::string convert(signed char const* str) { ++ auto reinterpreted = reinterpret_cast(str); ++ return Detail::convertIntoString( ++ StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ))); ++ } ++ }; ++ template ++ struct StringMaker { ++ static std::string convert(unsigned char const* str) { ++ auto reinterpreted = reinterpret_cast(str); ++ return Detail::convertIntoString( ++ StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ))); ++ } ++ }; ++ ++#if defined(CATCH_CONFIG_CPP17_BYTE) ++ template<> ++ struct StringMaker { ++ static std::string convert(std::byte value); ++ }; ++#endif // defined(CATCH_CONFIG_CPP17_BYTE) ++ template<> ++ struct StringMaker { ++ static std::string convert(int value); ++ }; ++ template<> ++ struct StringMaker { ++ static std::string convert(long value); ++ }; ++ template<> ++ struct StringMaker { ++ static std::string convert(long long value); ++ }; ++ template<> ++ struct StringMaker { ++ static std::string convert(unsigned int value); ++ }; ++ template<> ++ struct StringMaker { ++ static std::string convert(unsigned long value); ++ }; ++ template<> ++ struct StringMaker { ++ static std::string convert(unsigned long long value); ++ }; ++ ++ template<> ++ struct StringMaker { ++ static std::string convert(bool b) { ++ using namespace std::string_literals; ++ return b ? "true"s : "false"s; ++ } ++ }; ++ ++ template<> ++ struct StringMaker { ++ static std::string convert(char c); ++ }; ++ template<> ++ struct StringMaker { ++ static std::string convert(signed char value); ++ }; ++ template<> ++ struct StringMaker { ++ static std::string convert(unsigned char value); ++ }; ++ ++ template<> ++ struct StringMaker { ++ static std::string convert(std::nullptr_t) { ++ using namespace std::string_literals; ++ return "nullptr"s; ++ } ++ }; ++ ++ template<> ++ struct StringMaker { ++ static std::string convert(float value); ++ CATCH_EXPORT static int precision; ++ }; ++ ++ template<> ++ struct StringMaker { ++ static std::string convert(double value); ++ CATCH_EXPORT static int precision; ++ }; ++ ++ template ++ struct StringMaker { ++ template ++ static std::string convert(U* p) { ++ if (p) { ++ return ::Catch::Detail::rawMemoryToString(p); ++ } else { ++ return "nullptr"; ++ } ++ } ++ }; ++ ++ template ++ struct StringMaker { ++ static std::string convert(R C::* p) { ++ if (p) { ++ return ::Catch::Detail::rawMemoryToString(p); ++ } else { ++ return "nullptr"; ++ } ++ } ++ }; ++ ++#if defined(_MANAGED) ++ template ++ struct StringMaker { ++ static std::string convert( T^ ref ) { ++ return ::Catch::Detail::clrReferenceToString(ref); ++ } ++ }; ++#endif ++ ++ namespace Detail { ++ template ++ std::string rangeToString(InputIterator first, Sentinel last) { ++ ReusableStringStream rss; ++ rss << "{ "; ++ if (first != last) { ++ rss << ::Catch::Detail::stringify(*first); ++ for (++first; first != last; ++first) ++ rss << ", " << ::Catch::Detail::stringify(*first); ++ } ++ rss << " }"; ++ return rss.str(); ++ } ++ } ++ ++} // namespace Catch ++ ++////////////////////////////////////////////////////// ++// Separate std-lib types stringification, so it can be selectively enabled ++// This means that we do not bring in their headers ++ ++#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS) ++# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER ++# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER ++# define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER ++# define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER ++#endif ++ ++// Separate std::pair specialization ++#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) ++#include ++namespace Catch { ++ template ++ struct StringMaker > { ++ static std::string convert(const std::pair& pair) { ++ ReusableStringStream rss; ++ rss << "{ " ++ << ::Catch::Detail::stringify(pair.first) ++ << ", " ++ << ::Catch::Detail::stringify(pair.second) ++ << " }"; ++ return rss.str(); ++ } ++ }; ++} ++#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER ++ ++#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL) ++#include ++namespace Catch { ++ template ++ struct StringMaker > { ++ static std::string convert(const std::optional& optional) { ++ if (optional.has_value()) { ++ return ::Catch::Detail::stringify(*optional); ++ } else { ++ return "{ }"; ++ } ++ } ++ }; ++ template <> ++ struct StringMaker { ++ static std::string convert(const std::nullopt_t&) { ++ return "{ }"; ++ } ++ }; ++} ++#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER ++ ++// Separate std::tuple specialization ++#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) ++#include ++namespace Catch { ++ namespace Detail { ++ template< ++ typename Tuple, ++ std::size_t N = 0, ++ bool = (N < std::tuple_size::value) ++ > ++ struct TupleElementPrinter { ++ static void print(const Tuple& tuple, std::ostream& os) { ++ os << (N ? ", " : " ") ++ << ::Catch::Detail::stringify(std::get(tuple)); ++ TupleElementPrinter::print(tuple, os); ++ } ++ }; ++ ++ template< ++ typename Tuple, ++ std::size_t N ++ > ++ struct TupleElementPrinter { ++ static void print(const Tuple&, std::ostream&) {} ++ }; ++ ++ } ++ ++ ++ template ++ struct StringMaker> { ++ static std::string convert(const std::tuple& tuple) { ++ ReusableStringStream rss; ++ rss << '{'; ++ Detail::TupleElementPrinter>::print(tuple, rss.get()); ++ rss << " }"; ++ return rss.str(); ++ } ++ }; ++} ++#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER ++ ++#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) ++#include ++namespace Catch { ++ template<> ++ struct StringMaker { ++ static std::string convert(const std::monostate&) { ++ return "{ }"; ++ } ++ }; ++ ++ template ++ struct StringMaker> { ++ static std::string convert(const std::variant& variant) { ++ if (variant.valueless_by_exception()) { ++ return "{valueless variant}"; ++ } else { ++ return std::visit( ++ [](const auto& value) { ++ return ::Catch::Detail::stringify(value); ++ }, ++ variant ++ ); ++ } ++ } ++ }; ++} ++#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER ++ ++namespace Catch { ++ // Import begin/ end from std here ++ using std::begin; ++ using std::end; ++ ++ namespace Detail { ++ template ++ struct is_range_impl : std::false_type {}; ++ ++ template ++ struct is_range_impl()))>> : std::true_type {}; ++ } // namespace Detail ++ ++ template ++ struct is_range : Detail::is_range_impl {}; ++ ++#if defined(_MANAGED) // Managed types are never ranges ++ template ++ struct is_range { ++ static const bool value = false; ++ }; ++#endif ++ ++ template ++ std::string rangeToString( Range const& range ) { ++ return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); ++ } ++ ++ // Handle vector specially ++ template ++ std::string rangeToString( std::vector const& v ) { ++ ReusableStringStream rss; ++ rss << "{ "; ++ bool first = true; ++ for( bool b : v ) { ++ if( first ) ++ first = false; ++ else ++ rss << ", "; ++ rss << ::Catch::Detail::stringify( b ); ++ } ++ rss << " }"; ++ return rss.str(); ++ } ++ ++ template ++ struct StringMaker::value && !::Catch::Detail::IsStreamInsertable::value>> { ++ static std::string convert( R const& range ) { ++ return rangeToString( range ); ++ } ++ }; ++ ++ template ++ struct StringMaker { ++ static std::string convert(T const(&arr)[SZ]) { ++ return rangeToString(arr); ++ } ++ }; ++ ++ ++} // namespace Catch ++ ++// Separate std::chrono::duration specialization ++#include ++#include ++#include ++ ++ ++namespace Catch { ++ ++template ++struct ratio_string { ++ static std::string symbol() { ++ Catch::ReusableStringStream rss; ++ rss << '[' << Ratio::num << '/' ++ << Ratio::den << ']'; ++ return rss.str(); ++ } ++}; ++ ++template <> ++struct ratio_string { ++ static char symbol() { return 'a'; } ++}; ++template <> ++struct ratio_string { ++ static char symbol() { return 'f'; } ++}; ++template <> ++struct ratio_string { ++ static char symbol() { return 'p'; } ++}; ++template <> ++struct ratio_string { ++ static char symbol() { return 'n'; } ++}; ++template <> ++struct ratio_string { ++ static char symbol() { return 'u'; } ++}; ++template <> ++struct ratio_string { ++ static char symbol() { return 'm'; } ++}; ++ ++ //////////// ++ // std::chrono::duration specializations ++ template ++ struct StringMaker> { ++ static std::string convert(std::chrono::duration const& duration) { ++ ReusableStringStream rss; ++ rss << duration.count() << ' ' << ratio_string::symbol() << 's'; ++ return rss.str(); ++ } ++ }; ++ template ++ struct StringMaker>> { ++ static std::string convert(std::chrono::duration> const& duration) { ++ ReusableStringStream rss; ++ rss << duration.count() << " s"; ++ return rss.str(); ++ } ++ }; ++ template ++ struct StringMaker>> { ++ static std::string convert(std::chrono::duration> const& duration) { ++ ReusableStringStream rss; ++ rss << duration.count() << " m"; ++ return rss.str(); ++ } ++ }; ++ template ++ struct StringMaker>> { ++ static std::string convert(std::chrono::duration> const& duration) { ++ ReusableStringStream rss; ++ rss << duration.count() << " h"; ++ return rss.str(); ++ } ++ }; ++ ++ //////////// ++ // std::chrono::time_point specialization ++ // Generic time_point cannot be specialized, only std::chrono::time_point ++ template ++ struct StringMaker> { ++ static std::string convert(std::chrono::time_point const& time_point) { ++ return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; ++ } ++ }; ++ // std::chrono::time_point specialization ++ template ++ struct StringMaker> { ++ static std::string convert(std::chrono::time_point const& time_point) { ++ auto converted = std::chrono::system_clock::to_time_t(time_point); ++ ++#ifdef _MSC_VER ++ std::tm timeInfo = {}; ++ gmtime_s(&timeInfo, &converted); ++#else ++ std::tm* timeInfo = std::gmtime(&converted); ++#endif ++ ++ auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); ++ char timeStamp[timeStampSize]; ++ const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; ++ ++#ifdef _MSC_VER ++ std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); ++#else ++ std::strftime(timeStamp, timeStampSize, fmt, timeInfo); ++#endif ++ return std::string(timeStamp, timeStampSize - 1); ++ } ++ }; ++} ++ ++ ++#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \ ++namespace Catch { \ ++ template<> struct StringMaker { \ ++ static std::string convert( enumName value ) { \ ++ static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \ ++ return static_cast(enumInfo.lookup( static_cast( value ) )); \ ++ } \ ++ }; \ ++} ++ ++#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ ) ++ ++#ifdef _MSC_VER ++#pragma warning(pop) ++#endif ++ ++#endif // CATCH_TOSTRING_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ class Approx { ++ private: ++ bool equalityComparisonImpl(double other) const; ++ // Sets and validates the new margin (margin >= 0) ++ void setMargin(double margin); ++ // Sets and validates the new epsilon (0 < epsilon < 1) ++ void setEpsilon(double epsilon); ++ ++ public: ++ explicit Approx ( double value ); ++ ++ static Approx custom(); ++ ++ Approx operator-() const; ++ ++ template ::value>> ++ Approx operator()( T const& value ) const { ++ Approx approx( static_cast(value) ); ++ approx.m_epsilon = m_epsilon; ++ approx.m_margin = m_margin; ++ approx.m_scale = m_scale; ++ return approx; ++ } ++ ++ template ::value>> ++ explicit Approx( T const& value ): Approx(static_cast(value)) ++ {} ++ ++ ++ template ::value>> ++ friend bool operator == ( const T& lhs, Approx const& rhs ) { ++ auto lhs_v = static_cast(lhs); ++ return rhs.equalityComparisonImpl(lhs_v); ++ } ++ ++ template ::value>> ++ friend bool operator == ( Approx const& lhs, const T& rhs ) { ++ return operator==( rhs, lhs ); ++ } ++ ++ template ::value>> ++ friend bool operator != ( T const& lhs, Approx const& rhs ) { ++ return !operator==( lhs, rhs ); ++ } ++ ++ template ::value>> ++ friend bool operator != ( Approx const& lhs, T const& rhs ) { ++ return !operator==( rhs, lhs ); ++ } ++ ++ template ::value>> ++ friend bool operator <= ( T const& lhs, Approx const& rhs ) { ++ return static_cast(lhs) < rhs.m_value || lhs == rhs; ++ } ++ ++ template ::value>> ++ friend bool operator <= ( Approx const& lhs, T const& rhs ) { ++ return lhs.m_value < static_cast(rhs) || lhs == rhs; ++ } ++ ++ template ::value>> ++ friend bool operator >= ( T const& lhs, Approx const& rhs ) { ++ return static_cast(lhs) > rhs.m_value || lhs == rhs; ++ } ++ ++ template ::value>> ++ friend bool operator >= ( Approx const& lhs, T const& rhs ) { ++ return lhs.m_value > static_cast(rhs) || lhs == rhs; ++ } ++ ++ template ::value>> ++ Approx& epsilon( T const& newEpsilon ) { ++ const auto epsilonAsDouble = static_cast(newEpsilon); ++ setEpsilon(epsilonAsDouble); ++ return *this; ++ } ++ ++ template ::value>> ++ Approx& margin( T const& newMargin ) { ++ const auto marginAsDouble = static_cast(newMargin); ++ setMargin(marginAsDouble); ++ return *this; ++ } ++ ++ template ::value>> ++ Approx& scale( T const& newScale ) { ++ m_scale = static_cast(newScale); ++ return *this; ++ } ++ ++ std::string toString() const; ++ ++ private: ++ double m_epsilon; ++ double m_margin; ++ double m_scale; ++ double m_value; ++ }; ++ ++namespace literals { ++ Approx operator ""_a(long double val); ++ Approx operator ""_a(unsigned long long val); ++} // end namespace literals ++ ++template<> ++struct StringMaker { ++ static std::string convert(Catch::Approx const& value); ++}; ++ ++} // end namespace Catch ++ ++#endif // CATCH_APPROX_HPP_INCLUDED ++ ++ ++#ifndef CATCH_ASSERTION_INFO_HPP_INCLUDED ++#define CATCH_ASSERTION_INFO_HPP_INCLUDED ++ ++ ++ ++#ifndef CATCH_SOURCE_LINE_INFO_HPP_INCLUDED ++#define CATCH_SOURCE_LINE_INFO_HPP_INCLUDED ++ ++#include ++#include ++ ++namespace Catch { ++ ++ struct SourceLineInfo { ++ ++ SourceLineInfo() = delete; ++ constexpr SourceLineInfo( char const* _file, std::size_t _line ) noexcept: ++ file( _file ), ++ line( _line ) ++ {} ++ ++ bool operator == ( SourceLineInfo const& other ) const noexcept; ++ bool operator < ( SourceLineInfo const& other ) const noexcept; ++ ++ char const* file; ++ std::size_t line; ++ ++ friend std::ostream& operator << (std::ostream& os, SourceLineInfo const& info); ++ }; ++} ++ ++#define CATCH_INTERNAL_LINEINFO \ ++ ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) ++ ++#endif // CATCH_SOURCE_LINE_INFO_HPP_INCLUDED ++ ++namespace Catch { ++ ++ struct AssertionInfo { ++ // AssertionInfo() = delete; ++ ++ StringRef macroName; ++ SourceLineInfo lineInfo; ++ StringRef capturedExpression; ++ ResultDisposition::Flags resultDisposition; ++ }; ++ ++} // end namespace Catch ++ ++#endif // CATCH_ASSERTION_INFO_HPP_INCLUDED ++ ++ ++#ifndef CATCH_ASSERTION_RESULT_HPP_INCLUDED ++#define CATCH_ASSERTION_RESULT_HPP_INCLUDED ++ ++ ++ ++#ifndef CATCH_LAZY_EXPR_HPP_INCLUDED ++#define CATCH_LAZY_EXPR_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ class ITransientExpression; ++ ++ class LazyExpression { ++ friend class AssertionHandler; ++ friend struct AssertionStats; ++ friend class RunContext; ++ ++ ITransientExpression const* m_transientExpression = nullptr; ++ bool m_isNegated; ++ public: ++ LazyExpression( bool isNegated ): ++ m_isNegated(isNegated) ++ {} ++ LazyExpression(LazyExpression const& other) = default; ++ LazyExpression& operator = ( LazyExpression const& ) = delete; ++ ++ explicit operator bool() const { ++ return m_transientExpression != nullptr; ++ } ++ ++ friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; ++ }; ++ ++} // namespace Catch ++ ++#endif // CATCH_LAZY_EXPR_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ struct AssertionResultData ++ { ++ AssertionResultData() = delete; ++ ++ AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); ++ ++ std::string message; ++ mutable std::string reconstructedExpression; ++ LazyExpression lazyExpression; ++ ResultWas::OfType resultType; ++ ++ std::string reconstructExpression() const; ++ }; ++ ++ class AssertionResult { ++ public: ++ AssertionResult() = delete; ++ AssertionResult( AssertionInfo const& info, AssertionResultData&& data ); ++ ++ bool isOk() const; ++ bool succeeded() const; ++ ResultWas::OfType getResultType() const; ++ bool hasExpression() const; ++ bool hasMessage() const; ++ std::string getExpression() const; ++ std::string getExpressionInMacro() const; ++ bool hasExpandedExpression() const; ++ std::string getExpandedExpression() const; ++ StringRef getMessage() const; ++ SourceLineInfo getSourceInfo() const; ++ StringRef getTestMacroName() const; ++ ++ //protected: ++ AssertionInfo m_info; ++ AssertionResultData m_resultData; ++ }; ++ ++} // end namespace Catch ++ ++#endif // CATCH_ASSERTION_RESULT_HPP_INCLUDED ++ ++ ++#ifndef CATCH_CONFIG_HPP_INCLUDED ++#define CATCH_CONFIG_HPP_INCLUDED ++ ++ ++ ++#ifndef CATCH_TEST_SPEC_HPP_INCLUDED ++#define CATCH_TEST_SPEC_HPP_INCLUDED ++ ++#ifdef __clang__ ++#pragma clang diagnostic push ++#pragma clang diagnostic ignored "-Wpadded" ++#endif ++ ++ ++ ++#ifndef CATCH_WILDCARD_PATTERN_HPP_INCLUDED ++#define CATCH_WILDCARD_PATTERN_HPP_INCLUDED ++ ++ ++ ++#ifndef CATCH_CASE_SENSITIVE_HPP_INCLUDED ++#define CATCH_CASE_SENSITIVE_HPP_INCLUDED ++ ++namespace Catch { ++ ++ enum class CaseSensitive { Yes, No }; ++ ++} // namespace Catch ++ ++#endif // CATCH_CASE_SENSITIVE_HPP_INCLUDED ++ ++#include ++ ++namespace Catch ++{ ++ class WildcardPattern { ++ enum WildcardPosition { ++ NoWildcard = 0, ++ WildcardAtStart = 1, ++ WildcardAtEnd = 2, ++ WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd ++ }; ++ ++ public: ++ ++ WildcardPattern( std::string const& pattern, CaseSensitive caseSensitivity ); ++ bool matches( std::string const& str ) const; ++ ++ private: ++ std::string normaliseString( std::string const& str ) const; ++ CaseSensitive m_caseSensitivity; ++ WildcardPosition m_wildcard = NoWildcard; ++ std::string m_pattern; ++ }; ++} ++ ++#endif // CATCH_WILDCARD_PATTERN_HPP_INCLUDED ++ ++#include ++#include ++#include ++ ++namespace Catch { ++ ++ class IConfig; ++ struct TestCaseInfo; ++ class TestCaseHandle; ++ ++ class TestSpec { ++ ++ class Pattern { ++ public: ++ explicit Pattern( std::string const& name ); ++ virtual ~Pattern(); ++ virtual bool matches( TestCaseInfo const& testCase ) const = 0; ++ std::string const& name() const; ++ private: ++ virtual void serializeTo( std::ostream& out ) const = 0; ++ // Writes string that would be reparsed into the pattern ++ friend std::ostream& operator<<(std::ostream& out, ++ Pattern const& pattern) { ++ pattern.serializeTo( out ); ++ return out; ++ } ++ ++ std::string const m_name; ++ }; ++ ++ class NamePattern : public Pattern { ++ public: ++ explicit NamePattern( std::string const& name, std::string const& filterString ); ++ bool matches( TestCaseInfo const& testCase ) const override; ++ private: ++ void serializeTo( std::ostream& out ) const override; ++ ++ WildcardPattern m_wildcardPattern; ++ }; ++ ++ class TagPattern : public Pattern { ++ public: ++ explicit TagPattern( std::string const& tag, std::string const& filterString ); ++ bool matches( TestCaseInfo const& testCase ) const override; ++ private: ++ void serializeTo( std::ostream& out ) const override; ++ ++ std::string m_tag; ++ }; ++ ++ struct Filter { ++ std::vector> m_required; ++ std::vector> m_forbidden; ++ ++ //! Serializes this filter into a string that would be parsed into ++ //! an equivalent filter ++ void serializeTo( std::ostream& out ) const; ++ friend std::ostream& operator<<(std::ostream& out, Filter const& f) { ++ f.serializeTo( out ); ++ return out; ++ } ++ ++ bool matches( TestCaseInfo const& testCase ) const; ++ }; ++ ++ static std::string extractFilterName( Filter const& filter ); ++ ++ public: ++ struct FilterMatch { ++ std::string name; ++ std::vector tests; ++ }; ++ using Matches = std::vector; ++ using vectorStrings = std::vector; ++ ++ bool hasFilters() const; ++ bool matches( TestCaseInfo const& testCase ) const; ++ Matches matchesByFilter( std::vector const& testCases, IConfig const& config ) const; ++ const vectorStrings & getInvalidSpecs() const; ++ ++ private: ++ std::vector m_filters; ++ std::vector m_invalidSpecs; ++ ++ friend class TestSpecParser; ++ //! Serializes this test spec into a string that would be parsed into ++ //! equivalent test spec ++ void serializeTo( std::ostream& out ) const; ++ friend std::ostream& operator<<(std::ostream& out, ++ TestSpec const& spec) { ++ spec.serializeTo( out ); ++ return out; ++ } ++ }; ++} ++ ++#ifdef __clang__ ++#pragma clang diagnostic pop ++#endif ++ ++#endif // CATCH_TEST_SPEC_HPP_INCLUDED ++ ++ ++#ifndef CATCH_OPTIONAL_HPP_INCLUDED ++#define CATCH_OPTIONAL_HPP_INCLUDED ++ ++ ++#include ++ ++namespace Catch { ++ ++ // An optional type ++ template ++ class Optional { ++ public: ++ Optional(): nullableValue( nullptr ) {} ++ ~Optional() { reset(); } ++ ++ Optional( T const& _value ): ++ nullableValue( new ( storage ) T( _value ) ) {} ++ Optional( T&& _value ): ++ nullableValue( new ( storage ) T( CATCH_MOVE( _value ) ) ) {} ++ ++ Optional& operator=( T const& _value ) { ++ reset(); ++ nullableValue = new ( storage ) T( _value ); ++ return *this; ++ } ++ Optional& operator=( T&& _value ) { ++ reset(); ++ nullableValue = new ( storage ) T( CATCH_MOVE( _value ) ); ++ return *this; ++ } ++ ++ Optional( Optional const& _other ): ++ nullableValue( _other ? new ( storage ) T( *_other ) : nullptr ) {} ++ Optional( Optional&& _other ): ++ nullableValue( _other ? new ( storage ) T( CATCH_MOVE( *_other ) ) ++ : nullptr ) {} ++ ++ Optional& operator=( Optional const& _other ) { ++ if ( &_other != this ) { ++ reset(); ++ if ( _other ) { nullableValue = new ( storage ) T( *_other ); } ++ } ++ return *this; ++ } ++ Optional& operator=( Optional&& _other ) { ++ if ( &_other != this ) { ++ reset(); ++ if ( _other ) { ++ nullableValue = new ( storage ) T( CATCH_MOVE( *_other ) ); ++ } ++ } ++ return *this; ++ } ++ ++ void reset() { ++ if ( nullableValue ) { nullableValue->~T(); } ++ nullableValue = nullptr; ++ } ++ ++ T& operator*() { ++ assert(nullableValue); ++ return *nullableValue; ++ } ++ T const& operator*() const { ++ assert(nullableValue); ++ return *nullableValue; ++ } ++ T* operator->() { ++ assert(nullableValue); ++ return nullableValue; ++ } ++ const T* operator->() const { ++ assert(nullableValue); ++ return nullableValue; ++ } ++ ++ T valueOr( T const& defaultValue ) const { ++ return nullableValue ? *nullableValue : defaultValue; ++ } ++ ++ bool some() const { return nullableValue != nullptr; } ++ bool none() const { return nullableValue == nullptr; } ++ ++ bool operator !() const { return nullableValue == nullptr; } ++ explicit operator bool() const { ++ return some(); ++ } ++ ++ friend bool operator==(Optional const& a, Optional const& b) { ++ if (a.none() && b.none()) { ++ return true; ++ } else if (a.some() && b.some()) { ++ return *a == *b; ++ } else { ++ return false; ++ } ++ } ++ friend bool operator!=(Optional const& a, Optional const& b) { ++ return !( a == b ); ++ } ++ ++ private: ++ T* nullableValue; ++ alignas(alignof(T)) char storage[sizeof(T)]; ++ }; ++ ++} // end namespace Catch ++ ++#endif // CATCH_OPTIONAL_HPP_INCLUDED ++ ++ ++#ifndef CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED ++#define CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ enum class GenerateFrom { ++ Time, ++ RandomDevice, ++ //! Currently equivalent to RandomDevice, but can change at any point ++ Default ++ }; ++ ++ std::uint32_t generateRandomSeed(GenerateFrom from); ++ ++} // end namespace Catch ++ ++#endif // CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED ++ ++ ++#ifndef CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED ++#define CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED ++ ++ ++#include ++#include ++#include ++ ++namespace Catch { ++ ++ enum class ColourMode : std::uint8_t; ++ ++ namespace Detail { ++ //! Splits the reporter spec into reporter name and kv-pair options ++ std::vector splitReporterSpec( StringRef reporterSpec ); ++ ++ Optional stringToColourMode( StringRef colourMode ); ++ } ++ ++ /** ++ * Structured reporter spec that a reporter can be created from ++ * ++ * Parsing has been validated, but semantics have not. This means e.g. ++ * that the colour mode is known to Catch2, but it might not be ++ * compiled into the binary, and the output filename might not be ++ * openable. ++ */ ++ class ReporterSpec { ++ std::string m_name; ++ Optional m_outputFileName; ++ Optional m_colourMode; ++ std::map m_customOptions; ++ ++ friend bool operator==( ReporterSpec const& lhs, ++ ReporterSpec const& rhs ); ++ friend bool operator!=( ReporterSpec const& lhs, ++ ReporterSpec const& rhs ) { ++ return !( lhs == rhs ); ++ } ++ ++ public: ++ ReporterSpec( ++ std::string name, ++ Optional outputFileName, ++ Optional colourMode, ++ std::map customOptions ); ++ ++ std::string const& name() const { return m_name; } ++ ++ Optional const& outputFile() const { ++ return m_outputFileName; ++ } ++ ++ Optional const& colourMode() const { return m_colourMode; } ++ ++ std::map const& customOptions() const { ++ return m_customOptions; ++ } ++ }; ++ ++ /** ++ * Parses provided reporter spec string into ++ * ++ * Returns empty optional on errors, e.g. ++ * * field that is not first and not a key+value pair ++ * * duplicated keys in kv pair ++ * * unknown catch reporter option ++ * * empty key/value in an custom kv pair ++ * * ... ++ */ ++ Optional parseReporterSpec( StringRef reporterSpec ); ++ ++} ++ ++#endif // CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED ++ ++#include ++#include ++#include ++#include ++ ++namespace Catch { ++ ++ class IStream; ++ ++ /** ++ * `ReporterSpec` but with the defaults filled in. ++ * ++ * Like `ReporterSpec`, the semantics are unchecked. ++ */ ++ struct ProcessedReporterSpec { ++ std::string name; ++ std::string outputFilename; ++ ColourMode colourMode; ++ std::map customOptions; ++ friend bool operator==( ProcessedReporterSpec const& lhs, ++ ProcessedReporterSpec const& rhs ); ++ friend bool operator!=( ProcessedReporterSpec const& lhs, ++ ProcessedReporterSpec const& rhs ) { ++ return !( lhs == rhs ); ++ } ++ }; ++ ++ struct ConfigData { ++ ++ bool listTests = false; ++ bool listTags = false; ++ bool listReporters = false; ++ bool listListeners = false; ++ ++ bool showSuccessfulTests = false; ++ bool shouldDebugBreak = false; ++ bool noThrow = false; ++ bool showHelp = false; ++ bool showInvisibles = false; ++ bool filenamesAsTags = false; ++ bool libIdentify = false; ++ bool allowZeroTests = false; ++ ++ int abortAfter = -1; ++ uint32_t rngSeed = generateRandomSeed(GenerateFrom::Default); ++ ++ unsigned int shardCount = 1; ++ unsigned int shardIndex = 0; ++ ++ bool skipBenchmarks = false; ++ bool benchmarkNoAnalysis = false; ++ unsigned int benchmarkSamples = 100; ++ double benchmarkConfidenceInterval = 0.95; ++ unsigned int benchmarkResamples = 100'000; ++ std::chrono::milliseconds::rep benchmarkWarmupTime = 100; ++ ++ Verbosity verbosity = Verbosity::Normal; ++ WarnAbout::What warnings = WarnAbout::Nothing; ++ ShowDurations showDurations = ShowDurations::DefaultForReporter; ++ double minDuration = -1; ++ TestRunOrder runOrder = TestRunOrder::Declared; ++ ColourMode defaultColourMode = ColourMode::PlatformDefault; ++ WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; ++ ++ std::string defaultOutputFilename; ++ std::string name; ++ std::string processName; ++ std::vector reporterSpecifications; ++ ++ std::vector testsOrTags; ++ std::vector sectionsToRun; ++ }; ++ ++ ++ class Config : public IConfig { ++ public: ++ ++ Config() = default; ++ Config( ConfigData const& data ); ++ ~Config() override; // = default in the cpp file ++ ++ bool listTests() const; ++ bool listTags() const; ++ bool listReporters() const; ++ bool listListeners() const; ++ ++ std::vector const& getReporterSpecs() const; ++ std::vector const& ++ getProcessedReporterSpecs() const; ++ ++ std::vector const& getTestsOrTags() const override; ++ std::vector const& getSectionsToRun() const override; ++ ++ TestSpec const& testSpec() const override; ++ bool hasTestFilters() const override; ++ ++ bool showHelp() const; ++ ++ // IConfig interface ++ bool allowThrows() const override; ++ StringRef name() const override; ++ bool includeSuccessfulResults() const override; ++ bool warnAboutMissingAssertions() const override; ++ bool warnAboutUnmatchedTestSpecs() const override; ++ bool zeroTestsCountAsSuccess() const override; ++ ShowDurations showDurations() const override; ++ double minDuration() const override; ++ TestRunOrder runOrder() const override; ++ uint32_t rngSeed() const override; ++ unsigned int shardCount() const override; ++ unsigned int shardIndex() const override; ++ ColourMode defaultColourMode() const override; ++ bool shouldDebugBreak() const override; ++ int abortAfter() const override; ++ bool showInvisibles() const override; ++ Verbosity verbosity() const override; ++ bool skipBenchmarks() const override; ++ bool benchmarkNoAnalysis() const override; ++ unsigned int benchmarkSamples() const override; ++ double benchmarkConfidenceInterval() const override; ++ unsigned int benchmarkResamples() const override; ++ std::chrono::milliseconds benchmarkWarmupTime() const override; ++ ++ private: ++ // Reads Bazel env vars and applies them to the config ++ void readBazelEnvVars(); ++ ++ ConfigData m_data; ++ std::vector m_processedReporterSpecs; ++ TestSpec m_testSpec; ++ bool m_hasTestFilters = false; ++ }; ++} // end namespace Catch ++ ++#endif // CATCH_CONFIG_HPP_INCLUDED ++ ++ ++#ifndef CATCH_GET_RANDOM_SEED_HPP_INCLUDED ++#define CATCH_GET_RANDOM_SEED_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ //! Returns Catch2's current RNG seed. ++ std::uint32_t getSeed(); ++} ++ ++#endif // CATCH_GET_RANDOM_SEED_HPP_INCLUDED ++ ++ ++#ifndef CATCH_MESSAGE_HPP_INCLUDED ++#define CATCH_MESSAGE_HPP_INCLUDED ++ ++ ++ ++ ++/** \file ++ * Wrapper for the CATCH_CONFIG_PREFIX_MESSAGES configuration option ++ * ++ * CATCH_CONFIG_PREFIX_ALL can be used to avoid clashes with other macros ++ * by prepending CATCH_. This may not be desirable if the only clashes are with ++ * logger macros such as INFO and WARN. In this cases ++ * CATCH_CONFIG_PREFIX_MESSAGES can be used to only prefix a small subset ++ * of relevant macros. ++ * ++ */ ++ ++#ifndef CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED ++#define CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED ++ ++ ++#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_PREFIX_MESSAGES) ++ #define CATCH_CONFIG_PREFIX_MESSAGES ++#endif ++ ++#endif // CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED ++ ++ ++#ifndef CATCH_STREAM_END_STOP_HPP_INCLUDED ++#define CATCH_STREAM_END_STOP_HPP_INCLUDED ++ ++ ++namespace Catch { ++ ++ // Use this in variadic streaming macros to allow ++ // << +StreamEndStop ++ // as well as ++ // << stuff +StreamEndStop ++ struct StreamEndStop { ++ constexpr StringRef operator+() const { return StringRef(); } ++ ++ template ++ constexpr friend T const& operator+( T const& value, StreamEndStop ) { ++ return value; ++ } ++ }; ++ ++} // namespace Catch ++ ++#endif // CATCH_STREAM_END_STOP_HPP_INCLUDED ++ ++ ++#ifndef CATCH_MESSAGE_INFO_HPP_INCLUDED ++#define CATCH_MESSAGE_INFO_HPP_INCLUDED ++ ++ ++#include ++ ++namespace Catch { ++ ++ struct MessageInfo { ++ MessageInfo( StringRef _macroName, ++ SourceLineInfo const& _lineInfo, ++ ResultWas::OfType _type ); ++ ++ StringRef macroName; ++ std::string message; ++ SourceLineInfo lineInfo; ++ ResultWas::OfType type; ++ unsigned int sequence; ++ ++ bool operator == (MessageInfo const& other) const { ++ return sequence == other.sequence; ++ } ++ bool operator < (MessageInfo const& other) const { ++ return sequence < other.sequence; ++ } ++ private: ++ static unsigned int globalCount; ++ }; ++ ++} // end namespace Catch ++ ++#endif // CATCH_MESSAGE_INFO_HPP_INCLUDED ++ ++#include ++#include ++ ++namespace Catch { ++ ++ struct SourceLineInfo; ++ class IResultCapture; ++ ++ struct MessageStream { ++ ++ template ++ MessageStream& operator << ( T const& value ) { ++ m_stream << value; ++ return *this; ++ } ++ ++ ReusableStringStream m_stream; ++ }; ++ ++ struct MessageBuilder : MessageStream { ++ MessageBuilder( StringRef macroName, ++ SourceLineInfo const& lineInfo, ++ ResultWas::OfType type ): ++ m_info(macroName, lineInfo, type) {} ++ ++ template ++ MessageBuilder&& operator << ( T const& value ) && { ++ m_stream << value; ++ return CATCH_MOVE(*this); ++ } ++ ++ MessageInfo m_info; ++ }; ++ ++ class ScopedMessage { ++ public: ++ explicit ScopedMessage( MessageBuilder&& builder ); ++ ScopedMessage( ScopedMessage& duplicate ) = delete; ++ ScopedMessage( ScopedMessage&& old ) noexcept; ++ ~ScopedMessage(); ++ ++ MessageInfo m_info; ++ bool m_moved = false; ++ }; ++ ++ class Capturer { ++ std::vector m_messages; ++ IResultCapture& m_resultCapture; ++ size_t m_captured = 0; ++ public: ++ Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); ++ ++ Capturer(Capturer const&) = delete; ++ Capturer& operator=(Capturer const&) = delete; ++ ++ ~Capturer(); ++ ++ void captureValue( size_t index, std::string const& value ); ++ ++ template ++ void captureValues( size_t index, T const& value ) { ++ captureValue( index, Catch::Detail::stringify( value ) ); ++ } ++ ++ template ++ void captureValues( size_t index, T const& value, Ts const&... values ) { ++ captureValue( index, Catch::Detail::stringify(value) ); ++ captureValues( index+1, values... ); ++ } ++ }; ++ ++} // end namespace Catch ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ ++ do { \ ++ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \ ++ catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \ ++ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ ++ } while( false ) ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \ ++ Catch::Capturer varName( macroName##_catch_sr, \ ++ CATCH_INTERNAL_LINEINFO, \ ++ Catch::ResultWas::Info, \ ++ #__VA_ARGS__##_catch_sr ); \ ++ varName.captureValues( 0, __VA_ARGS__ ) ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define INTERNAL_CATCH_INFO( macroName, log ) \ ++ const Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \ ++ Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) ++ ++ ++#if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE) ++ ++ #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) ++ #define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg ) ++ #define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) ++ #define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE", __VA_ARGS__ ) ++ ++#elif defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE) ++ ++ #define CATCH_INFO( msg ) (void)(0) ++ #define CATCH_UNSCOPED_INFO( msg ) (void)(0) ++ #define CATCH_WARN( msg ) (void)(0) ++ #define CATCH_CAPTURE( ... ) (void)(0) ++ ++#elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE) ++ ++ #define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) ++ #define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg ) ++ #define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) ++ #define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE", __VA_ARGS__ ) ++ ++#elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE) ++ ++ #define INFO( msg ) (void)(0) ++ #define UNSCOPED_INFO( msg ) (void)(0) ++ #define WARN( msg ) (void)(0) ++ #define CAPTURE( ... ) (void)(0) ++ ++#endif // end of user facing macro declarations ++ ++ ++ ++ ++#endif // CATCH_MESSAGE_HPP_INCLUDED ++ ++ ++#ifndef CATCH_SECTION_INFO_HPP_INCLUDED ++#define CATCH_SECTION_INFO_HPP_INCLUDED ++ ++ ++ ++#ifndef CATCH_TOTALS_HPP_INCLUDED ++#define CATCH_TOTALS_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ struct Counts { ++ Counts operator - ( Counts const& other ) const; ++ Counts& operator += ( Counts const& other ); ++ ++ std::uint64_t total() const; ++ bool allPassed() const; ++ bool allOk() const; ++ ++ std::uint64_t passed = 0; ++ std::uint64_t failed = 0; ++ std::uint64_t failedButOk = 0; ++ std::uint64_t skipped = 0; ++ }; ++ ++ struct Totals { ++ ++ Totals operator - ( Totals const& other ) const; ++ Totals& operator += ( Totals const& other ); ++ ++ Totals delta( Totals const& prevTotals ) const; ++ ++ Counts assertions; ++ Counts testCases; ++ }; ++} ++ ++#endif // CATCH_TOTALS_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ struct SectionInfo { ++ // The last argument is ignored, so that people can write ++ // SECTION("ShortName", "Proper description that is long") and ++ // still use the `-c` flag comfortably. ++ SectionInfo( SourceLineInfo const& _lineInfo, std::string _name, ++ const char* const = nullptr ): ++ name(CATCH_MOVE(_name)), ++ lineInfo(_lineInfo) ++ {} ++ ++ std::string name; ++ SourceLineInfo lineInfo; ++ }; ++ ++ struct SectionEndInfo { ++ SectionInfo sectionInfo; ++ Counts prevAssertions; ++ double durationInSeconds; ++ }; ++ ++} // end namespace Catch ++ ++#endif // CATCH_SECTION_INFO_HPP_INCLUDED ++ ++ ++#ifndef CATCH_SESSION_HPP_INCLUDED ++#define CATCH_SESSION_HPP_INCLUDED ++ ++ ++ ++#ifndef CATCH_COMMANDLINE_HPP_INCLUDED ++#define CATCH_COMMANDLINE_HPP_INCLUDED ++ ++ ++ ++#ifndef CATCH_CLARA_HPP_INCLUDED ++#define CATCH_CLARA_HPP_INCLUDED ++ ++#if defined( __clang__ ) ++# pragma clang diagnostic push ++# pragma clang diagnostic ignored "-Wweak-vtables" ++# pragma clang diagnostic ignored "-Wshadow" ++# pragma clang diagnostic ignored "-Wdeprecated" ++#endif ++ ++#if defined( __GNUC__ ) ++# pragma GCC diagnostic push ++# pragma GCC diagnostic ignored "-Wsign-conversion" ++#endif ++ ++#ifndef CLARA_CONFIG_OPTIONAL_TYPE ++# ifdef __has_include ++# if __has_include( ) && __cplusplus >= 201703L ++# include ++# define CLARA_CONFIG_OPTIONAL_TYPE std::optional ++# endif ++# endif ++#endif ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++namespace Catch { ++ namespace Clara { ++ ++ class Args; ++ class Parser; ++ ++ // enum of result types from a parse ++ enum class ParseResultType { ++ Matched, ++ NoMatch, ++ ShortCircuitAll, ++ ShortCircuitSame ++ }; ++ ++ struct accept_many_t {}; ++ constexpr accept_many_t accept_many {}; ++ ++ namespace Detail { ++ struct fake_arg { ++ template ++ operator T(); ++ }; ++ ++ template ++ struct is_unary_function : std::false_type {}; ++ ++ template ++ struct is_unary_function< ++ F, ++ Catch::Detail::void_t()( fake_arg() ) ) ++ > ++ > : std::true_type {}; ++ ++ // Traits for extracting arg and return type of lambdas (for single ++ // argument lambdas) ++ template ++ struct UnaryLambdaTraits ++ : UnaryLambdaTraits {}; ++ ++ template ++ struct UnaryLambdaTraits { ++ static const bool isValid = false; ++ }; ++ ++ template ++ struct UnaryLambdaTraits { ++ static const bool isValid = true; ++ using ArgType = std::remove_const_t>; ++ using ReturnType = ReturnT; ++ }; ++ ++ class TokenStream; ++ ++ // Wraps a token coming from a token stream. These may not directly ++ // correspond to strings as a single string may encode an option + ++ // its argument if the : or = form is used ++ enum class TokenType { Option, Argument }; ++ struct Token { ++ TokenType type; ++ StringRef token; ++ }; ++ ++ // Abstracts iterators into args as a stream of tokens, with option ++ // arguments uniformly handled ++ class TokenStream { ++ using Iterator = std::vector::const_iterator; ++ Iterator it; ++ Iterator itEnd; ++ std::vector m_tokenBuffer; ++ void loadBuffer(); ++ ++ public: ++ explicit TokenStream( Args const& args ); ++ TokenStream( Iterator it, Iterator itEnd ); ++ ++ explicit operator bool() const { ++ return !m_tokenBuffer.empty() || it != itEnd; ++ } ++ ++ size_t count() const { ++ return m_tokenBuffer.size() + ( itEnd - it ); ++ } ++ ++ Token operator*() const { ++ assert( !m_tokenBuffer.empty() ); ++ return m_tokenBuffer.front(); ++ } ++ ++ Token const* operator->() const { ++ assert( !m_tokenBuffer.empty() ); ++ return &m_tokenBuffer.front(); ++ } ++ ++ TokenStream& operator++(); ++ }; ++ ++ //! Denotes type of a parsing result ++ enum class ResultType { ++ Ok, ///< No errors ++ LogicError, ///< Error in user-specified arguments for ++ ///< construction ++ RuntimeError ///< Error in parsing inputs ++ }; ++ ++ class ResultBase { ++ protected: ++ ResultBase( ResultType type ): m_type( type ) {} ++ virtual ~ResultBase(); // = default; ++ ++ ++ ResultBase(ResultBase const&) = default; ++ ResultBase& operator=(ResultBase const&) = default; ++ ResultBase(ResultBase&&) = default; ++ ResultBase& operator=(ResultBase&&) = default; ++ ++ virtual void enforceOk() const = 0; ++ ++ ResultType m_type; ++ }; ++ ++ template ++ class ResultValueBase : public ResultBase { ++ public: ++ T const& value() const& { ++ enforceOk(); ++ return m_value; ++ } ++ T&& value() && { ++ enforceOk(); ++ return CATCH_MOVE( m_value ); ++ } ++ ++ protected: ++ ResultValueBase( ResultType type ): ResultBase( type ) {} ++ ++ ResultValueBase( ResultValueBase const& other ): ++ ResultBase( other ) { ++ if ( m_type == ResultType::Ok ) ++ new ( &m_value ) T( other.m_value ); ++ } ++ ResultValueBase( ResultValueBase&& other ): ++ ResultBase( other ) { ++ if ( m_type == ResultType::Ok ) ++ new ( &m_value ) T( CATCH_MOVE(other.m_value) ); ++ } ++ ++ ++ ResultValueBase( ResultType, T const& value ): ++ ResultBase( ResultType::Ok ) { ++ new ( &m_value ) T( value ); ++ } ++ ResultValueBase( ResultType, T&& value ): ++ ResultBase( ResultType::Ok ) { ++ new ( &m_value ) T( CATCH_MOVE(value) ); ++ } ++ ++ ResultValueBase& operator=( ResultValueBase const& other ) { ++ if ( m_type == ResultType::Ok ) ++ m_value.~T(); ++ ResultBase::operator=( other ); ++ if ( m_type == ResultType::Ok ) ++ new ( &m_value ) T( other.m_value ); ++ return *this; ++ } ++ ResultValueBase& operator=( ResultValueBase&& other ) { ++ if ( m_type == ResultType::Ok ) m_value.~T(); ++ ResultBase::operator=( other ); ++ if ( m_type == ResultType::Ok ) ++ new ( &m_value ) T( CATCH_MOVE(other.m_value) ); ++ return *this; ++ } ++ ++ ++ ~ResultValueBase() override { ++ if ( m_type == ResultType::Ok ) ++ m_value.~T(); ++ } ++ ++ union { ++ T m_value; ++ }; ++ }; ++ ++ template <> class ResultValueBase : public ResultBase { ++ protected: ++ using ResultBase::ResultBase; ++ }; ++ ++ template ++ class BasicResult : public ResultValueBase { ++ public: ++ template ++ explicit BasicResult( BasicResult const& other ): ++ ResultValueBase( other.type() ), ++ m_errorMessage( other.errorMessage() ) { ++ assert( type() != ResultType::Ok ); ++ } ++ ++ template ++ static auto ok( U&& value ) -> BasicResult { ++ return { ResultType::Ok, CATCH_FORWARD(value) }; ++ } ++ static auto ok() -> BasicResult { return { ResultType::Ok }; } ++ static auto logicError( std::string&& message ) ++ -> BasicResult { ++ return { ResultType::LogicError, CATCH_MOVE(message) }; ++ } ++ static auto runtimeError( std::string&& message ) ++ -> BasicResult { ++ return { ResultType::RuntimeError, CATCH_MOVE(message) }; ++ } ++ ++ explicit operator bool() const { ++ return m_type == ResultType::Ok; ++ } ++ auto type() const -> ResultType { return m_type; } ++ auto errorMessage() const -> std::string const& { ++ return m_errorMessage; ++ } ++ ++ protected: ++ void enforceOk() const override { ++ ++ // Errors shouldn't reach this point, but if they do ++ // the actual error message will be in m_errorMessage ++ assert( m_type != ResultType::LogicError ); ++ assert( m_type != ResultType::RuntimeError ); ++ if ( m_type != ResultType::Ok ) ++ std::abort(); ++ } ++ ++ std::string ++ m_errorMessage; // Only populated if resultType is an error ++ ++ BasicResult( ResultType type, ++ std::string&& message ): ++ ResultValueBase( type ), m_errorMessage( CATCH_MOVE(message) ) { ++ assert( m_type != ResultType::Ok ); ++ } ++ ++ using ResultValueBase::ResultValueBase; ++ using ResultBase::m_type; ++ }; ++ ++ class ParseState { ++ public: ++ ParseState( ParseResultType type, ++ TokenStream remainingTokens ); ++ ++ ParseResultType type() const { return m_type; } ++ TokenStream const& remainingTokens() const& { ++ return m_remainingTokens; ++ } ++ TokenStream&& remainingTokens() && { ++ return CATCH_MOVE( m_remainingTokens ); ++ } ++ ++ private: ++ ParseResultType m_type; ++ TokenStream m_remainingTokens; ++ }; ++ ++ using Result = BasicResult; ++ using ParserResult = BasicResult; ++ using InternalParseResult = BasicResult; ++ ++ struct HelpColumns { ++ std::string left; ++ StringRef descriptions; ++ }; ++ ++ template ++ ParserResult convertInto( std::string const& source, T& target ) { ++ std::stringstream ss( source ); ++ ss >> target; ++ if ( ss.fail() ) { ++ return ParserResult::runtimeError( ++ "Unable to convert '" + source + ++ "' to destination type" ); ++ } else { ++ return ParserResult::ok( ParseResultType::Matched ); ++ } ++ } ++ ParserResult convertInto( std::string const& source, ++ std::string& target ); ++ ParserResult convertInto( std::string const& source, bool& target ); ++ ++#ifdef CLARA_CONFIG_OPTIONAL_TYPE ++ template ++ auto convertInto( std::string const& source, ++ CLARA_CONFIG_OPTIONAL_TYPE& target ) ++ -> ParserResult { ++ T temp; ++ auto result = convertInto( source, temp ); ++ if ( result ) ++ target = CATCH_MOVE( temp ); ++ return result; ++ } ++#endif // CLARA_CONFIG_OPTIONAL_TYPE ++ ++ struct BoundRef : Catch::Detail::NonCopyable { ++ virtual ~BoundRef() = default; ++ virtual bool isContainer() const; ++ virtual bool isFlag() const; ++ }; ++ struct BoundValueRefBase : BoundRef { ++ virtual auto setValue( std::string const& arg ) ++ -> ParserResult = 0; ++ }; ++ struct BoundFlagRefBase : BoundRef { ++ virtual auto setFlag( bool flag ) -> ParserResult = 0; ++ bool isFlag() const override; ++ }; ++ ++ template struct BoundValueRef : BoundValueRefBase { ++ T& m_ref; ++ ++ explicit BoundValueRef( T& ref ): m_ref( ref ) {} ++ ++ ParserResult setValue( std::string const& arg ) override { ++ return convertInto( arg, m_ref ); ++ } ++ }; ++ ++ template ++ struct BoundValueRef> : BoundValueRefBase { ++ std::vector& m_ref; ++ ++ explicit BoundValueRef( std::vector& ref ): m_ref( ref ) {} ++ ++ auto isContainer() const -> bool override { return true; } ++ ++ auto setValue( std::string const& arg ) ++ -> ParserResult override { ++ T temp; ++ auto result = convertInto( arg, temp ); ++ if ( result ) ++ m_ref.push_back( temp ); ++ return result; ++ } ++ }; ++ ++ struct BoundFlagRef : BoundFlagRefBase { ++ bool& m_ref; ++ ++ explicit BoundFlagRef( bool& ref ): m_ref( ref ) {} ++ ++ ParserResult setFlag( bool flag ) override; ++ }; ++ ++ template struct LambdaInvoker { ++ static_assert( ++ std::is_same::value, ++ "Lambda must return void or clara::ParserResult" ); ++ ++ template ++ static auto invoke( L const& lambda, ArgType const& arg ) ++ -> ParserResult { ++ return lambda( arg ); ++ } ++ }; ++ ++ template <> struct LambdaInvoker { ++ template ++ static auto invoke( L const& lambda, ArgType const& arg ) ++ -> ParserResult { ++ lambda( arg ); ++ return ParserResult::ok( ParseResultType::Matched ); ++ } ++ }; ++ ++ template ++ auto invokeLambda( L const& lambda, std::string const& arg ) ++ -> ParserResult { ++ ArgType temp{}; ++ auto result = convertInto( arg, temp ); ++ return !result ? result ++ : LambdaInvoker::ReturnType>::invoke( lambda, temp ); ++ } ++ ++ template struct BoundLambda : BoundValueRefBase { ++ L m_lambda; ++ ++ static_assert( ++ UnaryLambdaTraits::isValid, ++ "Supplied lambda must take exactly one argument" ); ++ explicit BoundLambda( L const& lambda ): m_lambda( lambda ) {} ++ ++ auto setValue( std::string const& arg ) ++ -> ParserResult override { ++ return invokeLambda::ArgType>( ++ m_lambda, arg ); ++ } ++ }; ++ ++ template struct BoundManyLambda : BoundLambda { ++ explicit BoundManyLambda( L const& lambda ): BoundLambda( lambda ) {} ++ bool isContainer() const override { return true; } ++ }; ++ ++ template struct BoundFlagLambda : BoundFlagRefBase { ++ L m_lambda; ++ ++ static_assert( ++ UnaryLambdaTraits::isValid, ++ "Supplied lambda must take exactly one argument" ); ++ static_assert( ++ std::is_same::ArgType, ++ bool>::value, ++ "flags must be boolean" ); ++ ++ explicit BoundFlagLambda( L const& lambda ): ++ m_lambda( lambda ) {} ++ ++ auto setFlag( bool flag ) -> ParserResult override { ++ return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); ++ } ++ }; ++ ++ enum class Optionality { Optional, Required }; ++ ++ class ParserBase { ++ public: ++ virtual ~ParserBase() = default; ++ virtual auto validate() const -> Result { return Result::ok(); } ++ virtual auto parse( std::string const& exeName, ++ TokenStream tokens ) const ++ -> InternalParseResult = 0; ++ virtual size_t cardinality() const; ++ ++ InternalParseResult parse( Args const& args ) const; ++ }; ++ ++ template ++ class ComposableParserImpl : public ParserBase { ++ public: ++ template ++ auto operator|( T const& other ) const -> Parser; ++ }; ++ ++ // Common code and state for Args and Opts ++ template ++ class ParserRefImpl : public ComposableParserImpl { ++ protected: ++ Optionality m_optionality = Optionality::Optional; ++ std::shared_ptr m_ref; ++ StringRef m_hint; ++ StringRef m_description; ++ ++ explicit ParserRefImpl( std::shared_ptr const& ref ): ++ m_ref( ref ) {} ++ ++ public: ++ template ++ ParserRefImpl( accept_many_t, ++ LambdaT const& ref, ++ StringRef hint ): ++ m_ref( std::make_shared>( ref ) ), ++ m_hint( hint ) {} ++ ++ template ::value>> ++ ParserRefImpl( T& ref, StringRef hint ): ++ m_ref( std::make_shared>( ref ) ), ++ m_hint( hint ) {} ++ ++ template ::value>> ++ ParserRefImpl( LambdaT const& ref, StringRef hint ): ++ m_ref( std::make_shared>( ref ) ), ++ m_hint( hint ) {} ++ ++ DerivedT& operator()( StringRef description ) & { ++ m_description = description; ++ return static_cast( *this ); ++ } ++ DerivedT&& operator()( StringRef description ) && { ++ m_description = description; ++ return static_cast( *this ); ++ } ++ ++ auto optional() -> DerivedT& { ++ m_optionality = Optionality::Optional; ++ return static_cast( *this ); ++ } ++ ++ auto required() -> DerivedT& { ++ m_optionality = Optionality::Required; ++ return static_cast( *this ); ++ } ++ ++ auto isOptional() const -> bool { ++ return m_optionality == Optionality::Optional; ++ } ++ ++ auto cardinality() const -> size_t override { ++ if ( m_ref->isContainer() ) ++ return 0; ++ else ++ return 1; ++ } ++ ++ StringRef hint() const { return m_hint; } ++ }; ++ ++ } // namespace detail ++ ++ ++ // A parser for arguments ++ class Arg : public Detail::ParserRefImpl { ++ public: ++ using ParserRefImpl::ParserRefImpl; ++ using ParserBase::parse; ++ ++ Detail::InternalParseResult ++ parse(std::string const&, ++ Detail::TokenStream tokens) const override; ++ }; ++ ++ // A parser for options ++ class Opt : public Detail::ParserRefImpl { ++ protected: ++ std::vector m_optNames; ++ ++ public: ++ template ++ explicit Opt(LambdaT const& ref) : ++ ParserRefImpl( ++ std::make_shared>(ref)) {} ++ ++ explicit Opt(bool& ref); ++ ++ template ::value>> ++ Opt( LambdaT const& ref, StringRef hint ): ++ ParserRefImpl( ref, hint ) {} ++ ++ template ++ Opt( accept_many_t, LambdaT const& ref, StringRef hint ): ++ ParserRefImpl( accept_many, ref, hint ) {} ++ ++ template ::value>> ++ Opt( T& ref, StringRef hint ): ++ ParserRefImpl( ref, hint ) {} ++ ++ Opt& operator[]( StringRef optName ) & { ++ m_optNames.push_back(optName); ++ return *this; ++ } ++ Opt&& operator[]( StringRef optName ) && { ++ m_optNames.push_back( optName ); ++ return CATCH_MOVE(*this); ++ } ++ ++ Detail::HelpColumns getHelpColumns() const; ++ ++ bool isMatch(StringRef optToken) const; ++ ++ using ParserBase::parse; ++ ++ Detail::InternalParseResult ++ parse(std::string const&, ++ Detail::TokenStream tokens) const override; ++ ++ Detail::Result validate() const override; ++ }; ++ ++ // Specifies the name of the executable ++ class ExeName : public Detail::ComposableParserImpl { ++ std::shared_ptr m_name; ++ std::shared_ptr m_ref; ++ ++ public: ++ ExeName(); ++ explicit ExeName(std::string& ref); ++ ++ template ++ explicit ExeName(LambdaT const& lambda) : ExeName() { ++ m_ref = std::make_shared>(lambda); ++ } ++ ++ // The exe name is not parsed out of the normal tokens, but is ++ // handled specially ++ Detail::InternalParseResult ++ parse(std::string const&, ++ Detail::TokenStream tokens) const override; ++ ++ std::string const& name() const { return *m_name; } ++ Detail::ParserResult set(std::string const& newName); ++ }; ++ ++ ++ // A Combined parser ++ class Parser : Detail::ParserBase { ++ mutable ExeName m_exeName; ++ std::vector m_options; ++ std::vector m_args; ++ ++ public: ++ ++ auto operator|=(ExeName const& exeName) -> Parser& { ++ m_exeName = exeName; ++ return *this; ++ } ++ ++ auto operator|=(Arg const& arg) -> Parser& { ++ m_args.push_back(arg); ++ return *this; ++ } ++ ++ friend Parser& operator|=( Parser& p, Opt const& opt ) { ++ p.m_options.push_back( opt ); ++ return p; ++ } ++ friend Parser& operator|=( Parser& p, Opt&& opt ) { ++ p.m_options.push_back( CATCH_MOVE(opt) ); ++ return p; ++ } ++ ++ Parser& operator|=(Parser const& other); ++ ++ template ++ friend Parser operator|( Parser const& p, T&& rhs ) { ++ Parser temp( p ); ++ temp |= rhs; ++ return temp; ++ } ++ ++ template ++ friend Parser operator|( Parser&& p, T&& rhs ) { ++ p |= CATCH_FORWARD(rhs); ++ return CATCH_MOVE(p); ++ } ++ ++ std::vector getHelpColumns() const; ++ ++ void writeToStream(std::ostream& os) const; ++ ++ friend auto operator<<(std::ostream& os, Parser const& parser) ++ -> std::ostream& { ++ parser.writeToStream(os); ++ return os; ++ } ++ ++ Detail::Result validate() const override; ++ ++ using ParserBase::parse; ++ Detail::InternalParseResult ++ parse(std::string const& exeName, ++ Detail::TokenStream tokens) const override; ++ }; ++ ++ /** ++ * Wrapper over argc + argv, assumes that the inputs outlive it ++ */ ++ class Args { ++ friend Detail::TokenStream; ++ StringRef m_exeName; ++ std::vector m_args; ++ ++ public: ++ Args(int argc, char const* const* argv); ++ // Helper constructor for testing ++ Args(std::initializer_list args); ++ ++ StringRef exeName() const { return m_exeName; } ++ }; ++ ++ ++ // Convenience wrapper for option parser that specifies the help option ++ struct Help : Opt { ++ Help(bool& showHelpFlag); ++ }; ++ ++ // Result type for parser operation ++ using Detail::ParserResult; ++ ++ namespace Detail { ++ template ++ template ++ Parser ++ ComposableParserImpl::operator|(T const& other) const { ++ return Parser() | static_cast(*this) | other; ++ } ++ } ++ ++ } // namespace Clara ++} // namespace Catch ++ ++#if defined( __clang__ ) ++# pragma clang diagnostic pop ++#endif ++ ++#if defined( __GNUC__ ) ++# pragma GCC diagnostic pop ++#endif ++ ++#endif // CATCH_CLARA_HPP_INCLUDED ++ ++namespace Catch { ++ ++ struct ConfigData; ++ ++ Clara::Parser makeCommandLineParser( ConfigData& config ); ++ ++} // end namespace Catch ++ ++#endif // CATCH_COMMANDLINE_HPP_INCLUDED ++ ++namespace Catch { ++ ++ class Session : Detail::NonCopyable { ++ public: ++ ++ Session(); ++ ~Session(); ++ ++ void showHelp() const; ++ void libIdentify(); ++ ++ int applyCommandLine( int argc, char const * const * argv ); ++ #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) ++ int applyCommandLine( int argc, wchar_t const * const * argv ); ++ #endif ++ ++ void useConfigData( ConfigData const& configData ); ++ ++ template ++ int run(int argc, CharT const * const argv[]) { ++ if (m_startupExceptions) ++ return 1; ++ int returnCode = applyCommandLine(argc, argv); ++ if (returnCode == 0) ++ returnCode = run(); ++ return returnCode; ++ } ++ ++ int run(); ++ ++ Clara::Parser const& cli() const; ++ void cli( Clara::Parser const& newParser ); ++ ConfigData& configData(); ++ Config& config(); ++ private: ++ int runInternal(); ++ ++ Clara::Parser m_cli; ++ ConfigData m_configData; ++ Detail::unique_ptr m_config; ++ bool m_startupExceptions = false; ++ }; ++ ++} // end namespace Catch ++ ++#endif // CATCH_SESSION_HPP_INCLUDED ++ ++ ++#ifndef CATCH_TAG_ALIAS_HPP_INCLUDED ++#define CATCH_TAG_ALIAS_HPP_INCLUDED ++ ++ ++#include ++ ++namespace Catch { ++ ++ struct TagAlias { ++ TagAlias(std::string const& _tag, SourceLineInfo _lineInfo): ++ tag(_tag), ++ lineInfo(_lineInfo) ++ {} ++ ++ std::string tag; ++ SourceLineInfo lineInfo; ++ }; ++ ++} // end namespace Catch ++ ++#endif // CATCH_TAG_ALIAS_HPP_INCLUDED ++ ++ ++#ifndef CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED ++#define CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED ++ ++ ++namespace Catch { ++ ++ struct RegistrarForTagAliases { ++ RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); ++ }; ++ ++} // end namespace Catch ++ ++#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ ++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ ++ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ ++ namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ ++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION ++ ++#endif // CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED ++ ++ ++#ifndef CATCH_TEMPLATE_TEST_MACROS_HPP_INCLUDED ++#define CATCH_TEMPLATE_TEST_MACROS_HPP_INCLUDED ++ ++// We need this suppression to leak, because it took until GCC 10 ++// for the front end to handle local suppression via _Pragma properly ++// inside templates (so `TEMPLATE_TEST_CASE` and co). ++// **THIS IS DIFFERENT FOR STANDARD TESTS, WHERE GCC 9 IS SUFFICIENT** ++#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ < 10 ++#pragma GCC diagnostic ignored "-Wparentheses" ++#endif ++ ++ ++ ++ ++#ifndef CATCH_TEST_MACROS_HPP_INCLUDED ++#define CATCH_TEST_MACROS_HPP_INCLUDED ++ ++ ++ ++#ifndef CATCH_TEST_MACRO_IMPL_HPP_INCLUDED ++#define CATCH_TEST_MACRO_IMPL_HPP_INCLUDED ++ ++ ++ ++#ifndef CATCH_ASSERTION_HANDLER_HPP_INCLUDED ++#define CATCH_ASSERTION_HANDLER_HPP_INCLUDED ++ ++ ++ ++#ifndef CATCH_DECOMPOSER_HPP_INCLUDED ++#define CATCH_DECOMPOSER_HPP_INCLUDED ++ ++ ++ ++#ifndef CATCH_COMPARE_TRAITS_HPP_INCLUDED ++#define CATCH_COMPARE_TRAITS_HPP_INCLUDED ++ ++ ++#include ++ ++namespace Catch { ++ namespace Detail { ++ ++#if defined( __GNUC__ ) && !defined( __clang__ ) ++# pragma GCC diagnostic push ++ // GCC likes to complain about comparing bool with 0, in the decltype() ++ // that defines the comparable traits below. ++# pragma GCC diagnostic ignored "-Wbool-compare" ++ // "ordered comparison of pointer with integer zero" same as above, ++ // but it does not have a separate warning flag to suppress ++# pragma GCC diagnostic ignored "-Wextra" ++ // Did you know that comparing floats with `0` directly ++ // is super-duper dangerous in unevaluated context? ++# pragma GCC diagnostic ignored "-Wfloat-equal" ++#endif ++ ++#if defined( __clang__ ) ++# pragma clang diagnostic push ++ // Did you know that comparing floats with `0` directly ++ // is super-duper dangerous in unevaluated context? ++# pragma clang diagnostic ignored "-Wfloat-equal" ++#endif ++ ++#define CATCH_DEFINE_COMPARABLE_TRAIT( id, op ) \ ++ template \ ++ struct is_##id##_comparable : std::false_type {}; \ ++ template \ ++ struct is_##id##_comparable< \ ++ T, \ ++ U, \ ++ void_t() op std::declval() )>> \ ++ : std::true_type {}; \ ++ template \ ++ struct is_##id##_0_comparable : std::false_type {}; \ ++ template \ ++ struct is_##id##_0_comparable() op 0 )>> \ ++ : std::true_type {}; ++ ++ // We need all 6 pre-spaceship comparison ops: <, <=, >, >=, ==, != ++ CATCH_DEFINE_COMPARABLE_TRAIT( lt, < ) ++ CATCH_DEFINE_COMPARABLE_TRAIT( le, <= ) ++ CATCH_DEFINE_COMPARABLE_TRAIT( gt, > ) ++ CATCH_DEFINE_COMPARABLE_TRAIT( ge, >= ) ++ CATCH_DEFINE_COMPARABLE_TRAIT( eq, == ) ++ CATCH_DEFINE_COMPARABLE_TRAIT( ne, != ) ++ ++#undef CATCH_DEFINE_COMPARABLE_TRAIT ++ ++#if defined( __GNUC__ ) && !defined( __clang__ ) ++# pragma GCC diagnostic pop ++#endif ++#if defined( __clang__ ) ++# pragma clang diagnostic pop ++#endif ++ ++ ++ } // namespace Detail ++} // namespace Catch ++ ++#endif // CATCH_COMPARE_TRAITS_HPP_INCLUDED ++ ++ ++#ifndef CATCH_LOGICAL_TRAITS_HPP_INCLUDED ++#define CATCH_LOGICAL_TRAITS_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++namespace Detail { ++ ++#if defined( __cpp_lib_logical_traits ) && __cpp_lib_logical_traits >= 201510 ++ ++ using std::conjunction; ++ using std::disjunction; ++ using std::negation; ++ ++#else ++ ++ template struct conjunction : std::true_type {}; ++ template struct conjunction : B1 {}; ++ template ++ struct conjunction ++ : std::conditional_t, B1> {}; ++ ++ template struct disjunction : std::false_type {}; ++ template struct disjunction : B1 {}; ++ template ++ struct disjunction ++ : std::conditional_t> {}; ++ ++ template ++ struct negation : std::integral_constant {}; ++ ++#endif ++ ++} // namespace Detail ++} // namespace Catch ++ ++#endif // CATCH_LOGICAL_TRAITS_HPP_INCLUDED ++ ++#include ++#include ++ ++/** \file ++ * Why does decomposing look the way it does: ++ * ++ * Conceptually, decomposing is simple. We change `REQUIRE( a == b )` into ++ * `Decomposer{} <= a == b`, so that `Decomposer{} <= a` is evaluated first, ++ * and our custom operator is used for `a == b`, because `a` is transformed ++ * into `ExprLhs` and then into `BinaryExpr`. ++ * ++ * In practice, decomposing ends up a mess, because we have to support ++ * various fun things. ++ * ++ * 1) Types that are only comparable with literal 0, and they do this by ++ * comparing against a magic type with pointer constructor and deleted ++ * other constructors. Example: `REQUIRE((a <=> b) == 0)` in libstdc++ ++ * ++ * 2) Types that are only comparable with literal 0, and they do this by ++ * comparing against a magic type with consteval integer constructor. ++ * Example: `REQUIRE((a <=> b) == 0)` in current MSVC STL. ++ * ++ * 3) Types that have no linkage, and so we cannot form a reference to ++ * them. Example: some implementations of traits. ++ * ++ * 4) Starting with C++20, when the compiler sees `a == b`, it also uses ++ * `b == a` when constructing the overload set. For us this means that ++ * when the compiler handles `ExprLhs == b`, it also tries to resolve ++ * the overload set for `b == ExprLhs`. ++ * ++ * To accomodate these use cases, decomposer ended up rather complex. ++ * ++ * 1) These types are handled by adding SFINAE overloads to our comparison ++ * operators, checking whether `T == U` are comparable with the given ++ * operator, and if not, whether T (or U) are comparable with literal 0. ++ * If yes, the overload compares T (or U) with 0 literal inline in the ++ * definition. ++ * ++ * Note that for extra correctness, we check that the other type is ++ * either an `int` (literal 0 is captured as `int` by templates), or ++ * a `long` (some platforms use 0L for `NULL` and we want to support ++ * that for pointer comparisons). ++ * ++ * 2) For these types, `is_foo_comparable` is true, but letting ++ * them fall into the overload that actually does `T == int` causes ++ * compilation error. Handling them requires that the decomposition ++ * is `constexpr`, so that P2564R3 applies and the `consteval` from ++ * their accompanying magic type is propagated through the `constexpr` ++ * call stack. ++ * ++ * However this is not enough to handle these types automatically, ++ * because our default is to capture types by reference, to avoid ++ * runtime copies. While these references cannot become dangling, ++ * they outlive the constexpr context and thus the default capture ++ * path cannot be actually constexpr. ++ * ++ * The solution is to capture these types by value, by explicitly ++ * specializing `Catch::capture_by_value` for them. Catch2 provides ++ * specialization for `std::foo_ordering`s, but users can specialize ++ * the trait for their own types as well. ++ * ++ * 3) If a type has no linkage, we also cannot capture it by reference. ++ * The solution is once again to capture them by value. We handle ++ * the common cases by using `std::is_arithmetic` as the default ++ * for `Catch::capture_by_value`, but that is only a some-effort ++ * heuristic. But as with 2), users can specialize `capture_by_value` ++ * for their own types as needed. ++ * ++ * 4) To support C++20 and make the SFINAE on our decomposing operators ++ * work, the SFINAE has to happen in return type, rather than in ++ * a template type. This is due to our use of logical type traits ++ * (`conjunction`/`disjunction`/`negation`), that we use to workaround ++ * an issue in older (9-) versions of GCC. I still blame C++20 for ++ * this, because without the comparison order switching, the logical ++ * traits could still be used in template type. ++ * ++ * There are also other side concerns, e.g. supporting both `REQUIRE(a)` ++ * and `REQUIRE(a == b)`, or making `REQUIRE_THAT(a, IsEqual(b))` slot ++ * nicely into the same expression handling logic, but these are rather ++ * straightforward and add only a bit of complexity (e.g. common base ++ * class for decomposed expressions). ++ */ ++ ++#ifdef _MSC_VER ++#pragma warning(push) ++#pragma warning(disable:4389) // '==' : signed/unsigned mismatch ++#pragma warning(disable:4018) // more "signed/unsigned mismatch" ++#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) ++#pragma warning(disable:4180) // qualifier applied to function type has no meaning ++#pragma warning(disable:4800) // Forcing result to true or false ++#endif ++ ++#ifdef __clang__ ++# pragma clang diagnostic push ++# pragma clang diagnostic ignored "-Wsign-compare" ++# pragma clang diagnostic ignored "-Wnon-virtual-dtor" ++#elif defined __GNUC__ ++# pragma GCC diagnostic push ++# pragma GCC diagnostic ignored "-Wsign-compare" ++# pragma GCC diagnostic ignored "-Wnon-virtual-dtor" ++#endif ++ ++#if defined(CATCH_CPP20_OR_GREATER) && __has_include() ++# include ++# if defined( __cpp_lib_three_way_comparison ) && \ ++ __cpp_lib_three_way_comparison >= 201907L ++# define CATCH_CONFIG_CPP20_COMPARE_OVERLOADS ++# endif ++#endif ++ ++namespace Catch { ++ ++ namespace Detail { ++ // This was added in C++20, but we require only C++14 for now. ++ template ++ using RemoveCVRef_t = std::remove_cv_t>; ++ } ++ ++ // Note: There is nothing that stops us from extending this, ++ // e.g. to `std::is_scalar`, but the more encompassing ++ // traits are usually also more expensive. For now we ++ // keep this as it used to be and it can be changed later. ++ template ++ struct capture_by_value ++ : std::integral_constant{}> {}; ++ ++#if defined( CATCH_CONFIG_CPP20_COMPARE_OVERLOADS ) ++ template <> ++ struct capture_by_value : std::true_type {}; ++ template <> ++ struct capture_by_value : std::true_type {}; ++ template <> ++ struct capture_by_value : std::true_type {}; ++#endif ++ ++ template ++ struct always_false : std::false_type {}; ++ ++ class ITransientExpression { ++ bool m_isBinaryExpression; ++ bool m_result; ++ ++ public: ++ constexpr auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } ++ constexpr auto getResult() const -> bool { return m_result; } ++ //! This function **has** to be overriden by the derived class. ++ virtual void streamReconstructedExpression( std::ostream& os ) const; ++ ++ constexpr ITransientExpression( bool isBinaryExpression, bool result ) ++ : m_isBinaryExpression( isBinaryExpression ), ++ m_result( result ) ++ {} ++ ++ ITransientExpression() = default; ++ ITransientExpression(ITransientExpression const&) = default; ++ ITransientExpression& operator=(ITransientExpression const&) = default; ++ ++ friend std::ostream& operator<<(std::ostream& out, ITransientExpression const& expr) { ++ expr.streamReconstructedExpression(out); ++ return out; ++ } ++ ++ protected: ++ ~ITransientExpression() = default; ++ }; ++ ++ void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); ++ ++ template ++ class BinaryExpr : public ITransientExpression { ++ LhsT m_lhs; ++ StringRef m_op; ++ RhsT m_rhs; ++ ++ void streamReconstructedExpression( std::ostream &os ) const override { ++ formatReconstructedExpression ++ ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); ++ } ++ ++ public: ++ constexpr BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) ++ : ITransientExpression{ true, comparisonResult }, ++ m_lhs( lhs ), ++ m_op( op ), ++ m_rhs( rhs ) ++ {} ++ ++ template ++ auto operator && ( T ) const -> BinaryExpr const { ++ static_assert(always_false::value, ++ "chained comparisons are not supported inside assertions, " ++ "wrap the expression inside parentheses, or decompose it"); ++ } ++ ++ template ++ auto operator || ( T ) const -> BinaryExpr const { ++ static_assert(always_false::value, ++ "chained comparisons are not supported inside assertions, " ++ "wrap the expression inside parentheses, or decompose it"); ++ } ++ ++ template ++ auto operator == ( T ) const -> BinaryExpr const { ++ static_assert(always_false::value, ++ "chained comparisons are not supported inside assertions, " ++ "wrap the expression inside parentheses, or decompose it"); ++ } ++ ++ template ++ auto operator != ( T ) const -> BinaryExpr const { ++ static_assert(always_false::value, ++ "chained comparisons are not supported inside assertions, " ++ "wrap the expression inside parentheses, or decompose it"); ++ } ++ ++ template ++ auto operator > ( T ) const -> BinaryExpr const { ++ static_assert(always_false::value, ++ "chained comparisons are not supported inside assertions, " ++ "wrap the expression inside parentheses, or decompose it"); ++ } ++ ++ template ++ auto operator < ( T ) const -> BinaryExpr const { ++ static_assert(always_false::value, ++ "chained comparisons are not supported inside assertions, " ++ "wrap the expression inside parentheses, or decompose it"); ++ } ++ ++ template ++ auto operator >= ( T ) const -> BinaryExpr const { ++ static_assert(always_false::value, ++ "chained comparisons are not supported inside assertions, " ++ "wrap the expression inside parentheses, or decompose it"); ++ } ++ ++ template ++ auto operator <= ( T ) const -> BinaryExpr const { ++ static_assert(always_false::value, ++ "chained comparisons are not supported inside assertions, " ++ "wrap the expression inside parentheses, or decompose it"); ++ } ++ }; ++ ++ template ++ class UnaryExpr : public ITransientExpression { ++ LhsT m_lhs; ++ ++ void streamReconstructedExpression( std::ostream &os ) const override { ++ os << Catch::Detail::stringify( m_lhs ); ++ } ++ ++ public: ++ explicit constexpr UnaryExpr( LhsT lhs ) ++ : ITransientExpression{ false, static_cast(lhs) }, ++ m_lhs( lhs ) ++ {} ++ }; ++ ++ ++ template ++ class ExprLhs { ++ LhsT m_lhs; ++ public: ++ explicit constexpr ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} ++ ++#define CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( id, op ) \ ++ template \ ++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ ++ -> std::enable_if_t< \ ++ Detail::conjunction, \ ++ Detail::negation>>>::value, \ ++ BinaryExpr> { \ ++ return { \ ++ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ ++ } \ ++ template \ ++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ ++ -> std::enable_if_t< \ ++ Detail::conjunction, \ ++ capture_by_value>::value, \ ++ BinaryExpr> { \ ++ return { \ ++ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ ++ } \ ++ template \ ++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ ++ -> std::enable_if_t< \ ++ Detail::conjunction< \ ++ Detail::negation>, \ ++ Detail::is_eq_0_comparable, \ ++ /* We allow long because we want `ptr op NULL` to be accepted */ \ ++ Detail::disjunction, \ ++ std::is_same>>::value, \ ++ BinaryExpr> { \ ++ if ( rhs != 0 ) { throw_test_failure_exception(); } \ ++ return { \ ++ static_cast( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \ ++ } \ ++ template \ ++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ ++ -> std::enable_if_t< \ ++ Detail::conjunction< \ ++ Detail::negation>, \ ++ Detail::is_eq_0_comparable, \ ++ /* We allow long because we want `ptr op NULL` to be accepted */ \ ++ Detail::disjunction, \ ++ std::is_same>>::value, \ ++ BinaryExpr> { \ ++ if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \ ++ return { static_cast( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ ++ } ++ ++ CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( eq, == ) ++ CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( ne, != ) ++ ++ #undef CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR ++ ++ ++#define CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( id, op ) \ ++ template \ ++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ ++ -> std::enable_if_t< \ ++ Detail::conjunction, \ ++ Detail::negation>>>::value, \ ++ BinaryExpr> { \ ++ return { \ ++ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ ++ } \ ++ template \ ++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ ++ -> std::enable_if_t< \ ++ Detail::conjunction, \ ++ capture_by_value>::value, \ ++ BinaryExpr> { \ ++ return { \ ++ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ ++ } \ ++ template \ ++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ ++ -> std::enable_if_t< \ ++ Detail::conjunction< \ ++ Detail::negation>, \ ++ Detail::is_##id##_0_comparable, \ ++ std::is_same>::value, \ ++ BinaryExpr> { \ ++ if ( rhs != 0 ) { throw_test_failure_exception(); } \ ++ return { \ ++ static_cast( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \ ++ } \ ++ template \ ++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ ++ -> std::enable_if_t< \ ++ Detail::conjunction< \ ++ Detail::negation>, \ ++ Detail::is_##id##_0_comparable, \ ++ std::is_same>::value, \ ++ BinaryExpr> { \ ++ if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \ ++ return { static_cast( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ ++ } ++ ++ CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( lt, < ) ++ CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( le, <= ) ++ CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( gt, > ) ++ CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( ge, >= ) ++ ++ #undef CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR ++ ++ ++#define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR( op ) \ ++ template \ ++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ ++ -> std::enable_if_t< \ ++ !capture_by_value>::value, \ ++ BinaryExpr> { \ ++ return { \ ++ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ ++ } \ ++ template \ ++ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ ++ -> std::enable_if_t::value, \ ++ BinaryExpr> { \ ++ return { \ ++ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ ++ } ++ ++ CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(|) ++ CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(&) ++ CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(^) ++ ++ #undef CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR ++ ++ template ++ friend auto operator && ( ExprLhs &&, RhsT && ) -> BinaryExpr { ++ static_assert(always_false::value, ++ "operator&& is not supported inside assertions, " ++ "wrap the expression inside parentheses, or decompose it"); ++ } ++ ++ template ++ friend auto operator || ( ExprLhs &&, RhsT && ) -> BinaryExpr { ++ static_assert(always_false::value, ++ "operator|| is not supported inside assertions, " ++ "wrap the expression inside parentheses, or decompose it"); ++ } ++ ++ constexpr auto makeUnaryExpr() const -> UnaryExpr { ++ return UnaryExpr{ m_lhs }; ++ } ++ }; ++ ++ struct Decomposer { ++ template >::value, ++ int> = 0> ++ constexpr friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs { ++ return ExprLhs{ lhs }; ++ } ++ ++ template ::value, int> = 0> ++ constexpr friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs { ++ return ExprLhs{ value }; ++ } ++ }; ++ ++} // end namespace Catch ++ ++#ifdef _MSC_VER ++#pragma warning(pop) ++#endif ++#ifdef __clang__ ++# pragma clang diagnostic pop ++#elif defined __GNUC__ ++# pragma GCC diagnostic pop ++#endif ++ ++#endif // CATCH_DECOMPOSER_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ struct AssertionReaction { ++ bool shouldDebugBreak = false; ++ bool shouldThrow = false; ++ bool shouldSkip = false; ++ }; ++ ++ class AssertionHandler { ++ AssertionInfo m_assertionInfo; ++ AssertionReaction m_reaction; ++ bool m_completed = false; ++ IResultCapture& m_resultCapture; ++ ++ public: ++ AssertionHandler ++ ( StringRef macroName, ++ SourceLineInfo const& lineInfo, ++ StringRef capturedExpression, ++ ResultDisposition::Flags resultDisposition ); ++ ~AssertionHandler() { ++ if ( !m_completed ) { ++ m_resultCapture.handleIncomplete( m_assertionInfo ); ++ } ++ } ++ ++ ++ template ++ void handleExpr( ExprLhs const& expr ) { ++ handleExpr( expr.makeUnaryExpr() ); ++ } ++ void handleExpr( ITransientExpression const& expr ); ++ ++ void handleMessage(ResultWas::OfType resultType, StringRef message); ++ ++ void handleExceptionThrownAsExpected(); ++ void handleUnexpectedExceptionNotThrown(); ++ void handleExceptionNotThrownAsExpected(); ++ void handleThrowingCallSkipped(); ++ void handleUnexpectedInflightException(); ++ ++ void complete(); ++ ++ // query ++ auto allowThrows() const -> bool; ++ }; ++ ++ void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str ); ++ ++} // namespace Catch ++ ++#endif // CATCH_ASSERTION_HANDLER_HPP_INCLUDED ++ ++ ++#ifndef CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED ++#define CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED ++ ++ ++#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) ++ #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__##_catch_sr ++#else ++ #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr ++#endif ++ ++#endif // CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED ++ ++// We need this suppression to leak, because it took until GCC 10 ++// for the front end to handle local suppression via _Pragma properly ++#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ <= 9 ++ #pragma GCC diagnostic ignored "-Wparentheses" ++#endif ++ ++#if !defined(CATCH_CONFIG_DISABLE) ++ ++#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Another way to speed-up compilation is to omit local try-catch for REQUIRE* ++// macros. ++#define INTERNAL_CATCH_TRY ++#define INTERNAL_CATCH_CATCH( capturer ) ++ ++#else // CATCH_CONFIG_FAST_COMPILE ++ ++#define INTERNAL_CATCH_TRY try ++#define INTERNAL_CATCH_CATCH( handler ) catch(...) { (handler).handleUnexpectedInflightException(); } ++ ++#endif ++ ++#define INTERNAL_CATCH_REACT( handler ) handler.complete(); ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ ++ do { /* NOLINT(bugprone-infinite-loop) */ \ ++ /* The expression should not be evaluated, but warnings should hopefully be checked */ \ ++ CATCH_INTERNAL_IGNORE_BUT_WARN(__VA_ARGS__); \ ++ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ ++ INTERNAL_CATCH_TRY { \ ++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ ++ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ ++ catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); /* NOLINT(bugprone-chained-comparison) */ \ ++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ ++ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ ++ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ ++ } while( (void)0, (false) && static_cast( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look ++ // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ ++ INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ ++ if( Catch::getResultCapture().lastAssertionPassed() ) ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \ ++ INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ ++ if( !Catch::getResultCapture().lastAssertionPassed() ) ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ ++ do { \ ++ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ ++ try { \ ++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ ++ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ ++ static_cast(__VA_ARGS__); \ ++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ ++ catchAssertionHandler.handleExceptionNotThrownAsExpected(); \ ++ } \ ++ catch( ... ) { \ ++ catchAssertionHandler.handleUnexpectedInflightException(); \ ++ } \ ++ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ ++ } while( false ) ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ ++ do { \ ++ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ ++ if( catchAssertionHandler.allowThrows() ) \ ++ try { \ ++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ ++ CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ ++ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ ++ static_cast(__VA_ARGS__); \ ++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ ++ catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ ++ } \ ++ catch( ... ) { \ ++ catchAssertionHandler.handleExceptionThrownAsExpected(); \ ++ } \ ++ else \ ++ catchAssertionHandler.handleThrowingCallSkipped(); \ ++ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ ++ } while( false ) ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ ++ do { \ ++ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ ++ if( catchAssertionHandler.allowThrows() ) \ ++ try { \ ++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ ++ CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ ++ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ ++ static_cast(expr); \ ++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ ++ catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ ++ } \ ++ catch( exceptionType const& ) { \ ++ catchAssertionHandler.handleExceptionThrownAsExpected(); \ ++ } \ ++ catch( ... ) { \ ++ catchAssertionHandler.handleUnexpectedInflightException(); \ ++ } \ ++ else \ ++ catchAssertionHandler.handleThrowingCallSkipped(); \ ++ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ ++ } while( false ) ++ ++ ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Although this is matcher-based, it can be used with just a string ++#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \ ++ do { \ ++ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ ++ if( catchAssertionHandler.allowThrows() ) \ ++ try { \ ++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ ++ CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ ++ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ ++ static_cast(__VA_ARGS__); \ ++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ ++ catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ ++ } \ ++ catch( ... ) { \ ++ Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher ); \ ++ } \ ++ else \ ++ catchAssertionHandler.handleThrowingCallSkipped(); \ ++ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ ++ } while( false ) ++ ++#endif // CATCH_CONFIG_DISABLE ++ ++#endif // CATCH_TEST_MACRO_IMPL_HPP_INCLUDED ++ ++ ++#ifndef CATCH_SECTION_HPP_INCLUDED ++#define CATCH_SECTION_HPP_INCLUDED ++ ++ ++ ++ ++/** \file ++ * Wrapper for the STATIC_ANALYSIS_SUPPORT configuration option ++ * ++ * Some of Catch2's macros can be defined differently to work better with ++ * static analysis tools, like clang-tidy or coverity. ++ * Currently the main use case is to show that `SECTION`s are executed ++ * exclusively, and not all in one run of a `TEST_CASE`. ++ */ ++ ++#ifndef CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED ++#define CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED ++ ++ ++#if defined(__clang_analyzer__) || defined(__COVERITY__) ++ #define CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT ++#endif ++ ++#if defined( CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT ) && \ ++ !defined( CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) && \ ++ !defined( CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) ++# define CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ++#endif ++ ++ ++#endif // CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED ++ ++ ++#ifndef CATCH_TIMER_HPP_INCLUDED ++#define CATCH_TIMER_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ class Timer { ++ uint64_t m_nanoseconds = 0; ++ public: ++ void start(); ++ auto getElapsedNanoseconds() const -> uint64_t; ++ auto getElapsedMicroseconds() const -> uint64_t; ++ auto getElapsedMilliseconds() const -> unsigned int; ++ auto getElapsedSeconds() const -> double; ++ }; ++ ++} // namespace Catch ++ ++#endif // CATCH_TIMER_HPP_INCLUDED ++ ++namespace Catch { ++ ++ class Section : Detail::NonCopyable { ++ public: ++ Section( SectionInfo&& info ); ++ Section( SourceLineInfo const& _lineInfo, ++ StringRef _name, ++ const char* const = nullptr ); ++ ~Section(); ++ ++ // This indicates whether the section should be executed or not ++ explicit operator bool() const; ++ ++ private: ++ SectionInfo m_info; ++ ++ Counts m_assertions; ++ bool m_sectionIncluded; ++ Timer m_timer; ++ }; ++ ++} // end namespace Catch ++ ++#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT) ++# define INTERNAL_CATCH_SECTION( ... ) \ ++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ ++ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ ++ if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \ ++ catch_internal_Section ) = \ ++ Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ ++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION ++ ++# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ ++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ ++ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ ++ if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \ ++ catch_internal_Section ) = \ ++ Catch::SectionInfo( \ ++ CATCH_INTERNAL_LINEINFO, \ ++ ( Catch::ReusableStringStream() << __VA_ARGS__ ) \ ++ .str() ) ) \ ++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION ++ ++#else ++ ++// These section definitions imply that at most one section at one level ++// will be intered (because only one section's __LINE__ can be equal to ++// the dummy `catchInternalSectionHint` variable from `TEST_CASE`). ++ ++namespace Catch { ++ namespace Detail { ++ // Intentionally without linkage, as it should only be used as a dummy ++ // symbol for static analysis. ++ // The arguments are used as a dummy for checking warnings in the passed ++ // expressions. ++ int GetNewSectionHint( StringRef, const char* const = nullptr ); ++ } // namespace Detail ++} // namespace Catch ++ ++ ++# define INTERNAL_CATCH_SECTION( ... ) \ ++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ ++ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ ++ CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ ++ if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \ ++ catchInternalSectionHint, \ ++ catchInternalSectionHint = \ ++ Catch::Detail::GetNewSectionHint(__VA_ARGS__); \ ++ catchInternalPreviousSectionHint == __LINE__ ) \ ++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION ++ ++# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ ++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ ++ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ ++ CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ ++ if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \ ++ catchInternalSectionHint, \ ++ catchInternalSectionHint = Catch::Detail::GetNewSectionHint( \ ++ ( Catch::ReusableStringStream() << __VA_ARGS__ ).str()); \ ++ catchInternalPreviousSectionHint == __LINE__ ) \ ++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION ++ ++#endif ++ ++ ++#endif // CATCH_SECTION_HPP_INCLUDED ++ ++ ++#ifndef CATCH_TEST_REGISTRY_HPP_INCLUDED ++#define CATCH_TEST_REGISTRY_HPP_INCLUDED ++ ++ ++ ++#ifndef CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED ++#define CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED ++ ++namespace Catch { ++ ++ class ITestInvoker { ++ public: ++ virtual void invoke() const = 0; ++ virtual ~ITestInvoker(); // = default ++ }; ++ ++} // namespace Catch ++ ++#endif // CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED ++ ++ ++#ifndef CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED ++#define CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED ++ ++#define INTERNAL_CATCH_EXPAND1( param ) INTERNAL_CATCH_EXPAND2( param ) ++#define INTERNAL_CATCH_EXPAND2( ... ) INTERNAL_CATCH_NO##__VA_ARGS__ ++#define INTERNAL_CATCH_DEF( ... ) INTERNAL_CATCH_DEF __VA_ARGS__ ++#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF ++ ++#define INTERNAL_CATCH_REMOVE_PARENS( ... ) \ ++ INTERNAL_CATCH_EXPAND1( INTERNAL_CATCH_DEF __VA_ARGS__ ) ++ ++#endif // CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED ++ ++// GCC 5 and older do not properly handle disabling unused-variable warning ++// with a _Pragma. This means that we have to leak the suppression to the ++// user code as well :-( ++#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 5 ++#pragma GCC diagnostic ignored "-Wunused-variable" ++#endif ++ ++ ++ ++namespace Catch { ++ ++template ++class TestInvokerAsMethod : public ITestInvoker { ++ void (C::*m_testAsMethod)(); ++public: ++ TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {} ++ ++ void invoke() const override { ++ C obj; ++ (obj.*m_testAsMethod)(); ++ } ++}; ++ ++Detail::unique_ptr makeTestInvoker( void(*testAsFunction)() ); ++ ++template ++Detail::unique_ptr makeTestInvoker( void (C::*testAsMethod)() ) { ++ return Detail::make_unique>( testAsMethod ); ++} ++ ++struct NameAndTags { ++ constexpr NameAndTags( StringRef name_ = StringRef(), ++ StringRef tags_ = StringRef() ) noexcept: ++ name( name_ ), tags( tags_ ) {} ++ StringRef name; ++ StringRef tags; ++}; ++ ++struct AutoReg : Detail::NonCopyable { ++ AutoReg( Detail::unique_ptr invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept; ++}; ++ ++} // end namespace Catch ++ ++#if defined(CATCH_CONFIG_DISABLE) ++ #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ ++ static inline void TestName() ++ #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ ++ namespace{ \ ++ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ ++ void test(); \ ++ }; \ ++ } \ ++ void TestName::test() ++#endif ++ ++ ++#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT) ++ ++ /////////////////////////////////////////////////////////////////////////////// ++ #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ ++ static void TestName(); \ ++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ ++ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ ++ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ ++ namespace{ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ ++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ ++ static void TestName() ++ #define INTERNAL_CATCH_TESTCASE( ... ) \ ++ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__ ) ++ ++#else // ^^ !CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT | vv CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ++ ++ ++// Dummy registrator for the dumy test case macros ++namespace Catch { ++ namespace Detail { ++ struct DummyUse { ++ DummyUse( void ( * )( int ), Catch::NameAndTags const& ); ++ }; ++ } // namespace Detail ++} // namespace Catch ++ ++// Note that both the presence of the argument and its exact name are ++// necessary for the section support. ++ ++// We provide a shadowed variable so that a `SECTION` inside non-`TEST_CASE` ++// tests can compile. The redefined `TEST_CASE` shadows this with param. ++static int catchInternalSectionHint = 0; ++ ++# define INTERNAL_CATCH_TESTCASE2( fname, ... ) \ ++ static void fname( int ); \ ++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ ++ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ ++ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ ++ static const Catch::Detail::DummyUse INTERNAL_CATCH_UNIQUE_NAME( \ ++ dummyUser )( &(fname), Catch::NameAndTags{ __VA_ARGS__ } ); \ ++ CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ ++ static void fname( [[maybe_unused]] int catchInternalSectionHint ) \ ++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION ++# define INTERNAL_CATCH_TESTCASE( ... ) \ ++ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( dummyFunction ), __VA_ARGS__ ) ++ ++ ++#endif // CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ++ ++ /////////////////////////////////////////////////////////////////////////////// ++ #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ ++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ ++ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ ++ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ ++ namespace{ \ ++ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ ++ void test(); \ ++ }; \ ++ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ ++ Catch::makeTestInvoker( &TestName::test ), \ ++ CATCH_INTERNAL_LINEINFO, \ ++ #ClassName##_catch_sr, \ ++ Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ ++ } \ ++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ ++ void TestName::test() ++ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ ++ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ ) ++ ++ ++ /////////////////////////////////////////////////////////////////////////////// ++ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ ++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ ++ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ ++ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ ++ namespace { \ ++ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ ++ Catch::makeTestInvoker( &QualifiedMethod ), \ ++ CATCH_INTERNAL_LINEINFO, \ ++ "&" #QualifiedMethod##_catch_sr, \ ++ Catch::NameAndTags{ __VA_ARGS__ } ); \ ++ } /* NOLINT */ \ ++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION ++ ++ ++ /////////////////////////////////////////////////////////////////////////////// ++ #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ ++ do { \ ++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ ++ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ ++ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ ++ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ ++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ ++ } while(false) ++ ++ ++#endif // CATCH_TEST_REGISTRY_HPP_INCLUDED ++ ++ ++// All of our user-facing macros support configuration toggle, that ++// forces them to be defined prefixed with CATCH_. We also like to ++// support another toggle that can minimize (disable) their implementation. ++// Given this, we have 4 different configuration options below ++ ++#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) ++ ++ #define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) ++ #define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) ++ ++ #define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) ++ #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) ++ #define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) ++ ++ #define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) ++ #define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) ++ #define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) ++ #define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) ++ #define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) ++ ++ #define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) ++ #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) ++ #define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) ++ ++ #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) ++ #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) ++ #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) ++ #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) ++ #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) ++ #define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) ++ #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) ++ #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) ++ #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) ++ #define CATCH_SKIP( ... ) INTERNAL_CATCH_MSG( "SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__ ) ++ ++ ++ #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) ++ #define CATCH_STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ ) ++ #define CATCH_STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ ) ++ #define CATCH_STATIC_CHECK( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ ) ++ #define CATCH_STATIC_CHECK_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ ) ++ #else ++ #define CATCH_STATIC_REQUIRE( ... ) CATCH_REQUIRE( __VA_ARGS__ ) ++ #define CATCH_STATIC_REQUIRE_FALSE( ... ) CATCH_REQUIRE_FALSE( __VA_ARGS__ ) ++ #define CATCH_STATIC_CHECK( ... ) CATCH_CHECK( __VA_ARGS__ ) ++ #define CATCH_STATIC_CHECK_FALSE( ... ) CATCH_CHECK_FALSE( __VA_ARGS__ ) ++ #endif ++ ++ ++ // "BDD-style" convenience wrappers ++ #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) ++ #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) ++ #define CATCH_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) ++ #define CATCH_AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) ++ #define CATCH_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) ++ #define CATCH_AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) ++ #define CATCH_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) ++ #define CATCH_AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) ++ ++#elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) // ^^ prefixed, implemented | vv prefixed, disabled ++ ++ #define CATCH_REQUIRE( ... ) (void)(0) ++ #define CATCH_REQUIRE_FALSE( ... ) (void)(0) ++ ++ #define CATCH_REQUIRE_THROWS( ... ) (void)(0) ++ #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) ++ #define CATCH_REQUIRE_NOTHROW( ... ) (void)(0) ++ ++ #define CATCH_CHECK( ... ) (void)(0) ++ #define CATCH_CHECK_FALSE( ... ) (void)(0) ++ #define CATCH_CHECKED_IF( ... ) if (__VA_ARGS__) ++ #define CATCH_CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) ++ #define CATCH_CHECK_NOFAIL( ... ) (void)(0) ++ ++ #define CATCH_CHECK_THROWS( ... ) (void)(0) ++ #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0) ++ #define CATCH_CHECK_NOTHROW( ... ) (void)(0) ++ ++ #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) ++ #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) ++ #define CATCH_METHOD_AS_TEST_CASE( method, ... ) ++ #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) ++ #define CATCH_SECTION( ... ) ++ #define CATCH_DYNAMIC_SECTION( ... ) ++ #define CATCH_FAIL( ... ) (void)(0) ++ #define CATCH_FAIL_CHECK( ... ) (void)(0) ++ #define CATCH_SUCCEED( ... ) (void)(0) ++ #define CATCH_SKIP( ... ) (void)(0) ++ ++ #define CATCH_STATIC_REQUIRE( ... ) (void)(0) ++ #define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0) ++ #define CATCH_STATIC_CHECK( ... ) (void)(0) ++ #define CATCH_STATIC_CHECK_FALSE( ... ) (void)(0) ++ ++ // "BDD-style" convenience wrappers ++ #define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) ++ #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), className ) ++ #define CATCH_GIVEN( desc ) ++ #define CATCH_AND_GIVEN( desc ) ++ #define CATCH_WHEN( desc ) ++ #define CATCH_AND_WHEN( desc ) ++ #define CATCH_THEN( desc ) ++ #define CATCH_AND_THEN( desc ) ++ ++#elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) // ^^ prefixed, disabled | vv unprefixed, implemented ++ ++ #define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) ++ #define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) ++ ++ #define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) ++ #define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) ++ #define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) ++ ++ #define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) ++ #define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) ++ #define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) ++ #define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) ++ #define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) ++ ++ #define CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) ++ #define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) ++ #define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) ++ ++ #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) ++ #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) ++ #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) ++ #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) ++ #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) ++ #define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) ++ #define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) ++ #define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) ++ #define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) ++ #define SKIP( ... ) INTERNAL_CATCH_MSG( "SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__ ) ++ ++ ++ #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) ++ #define STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ ) ++ #define STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" ) ++ #define STATIC_CHECK( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ ) ++ #define STATIC_CHECK_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" ) ++ #else ++ #define STATIC_REQUIRE( ... ) REQUIRE( __VA_ARGS__ ) ++ #define STATIC_REQUIRE_FALSE( ... ) REQUIRE_FALSE( __VA_ARGS__ ) ++ #define STATIC_CHECK( ... ) CHECK( __VA_ARGS__ ) ++ #define STATIC_CHECK_FALSE( ... ) CHECK_FALSE( __VA_ARGS__ ) ++ #endif ++ ++ // "BDD-style" convenience wrappers ++ #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) ++ #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) ++ #define GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) ++ #define AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) ++ #define WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) ++ #define AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) ++ #define THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) ++ #define AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) ++ ++#elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) // ^^ unprefixed, implemented | vv unprefixed, disabled ++ ++ #define REQUIRE( ... ) (void)(0) ++ #define REQUIRE_FALSE( ... ) (void)(0) ++ ++ #define REQUIRE_THROWS( ... ) (void)(0) ++ #define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) ++ #define REQUIRE_NOTHROW( ... ) (void)(0) ++ ++ #define CHECK( ... ) (void)(0) ++ #define CHECK_FALSE( ... ) (void)(0) ++ #define CHECKED_IF( ... ) if (__VA_ARGS__) ++ #define CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) ++ #define CHECK_NOFAIL( ... ) (void)(0) ++ ++ #define CHECK_THROWS( ... ) (void)(0) ++ #define CHECK_THROWS_AS( expr, exceptionType ) (void)(0) ++ #define CHECK_NOTHROW( ... ) (void)(0) ++ ++ #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__) ++ #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) ++ #define METHOD_AS_TEST_CASE( method, ... ) ++ #define REGISTER_TEST_CASE( Function, ... ) (void)(0) ++ #define SECTION( ... ) ++ #define DYNAMIC_SECTION( ... ) ++ #define FAIL( ... ) (void)(0) ++ #define FAIL_CHECK( ... ) (void)(0) ++ #define SUCCEED( ... ) (void)(0) ++ #define SKIP( ... ) (void)(0) ++ ++ #define STATIC_REQUIRE( ... ) (void)(0) ++ #define STATIC_REQUIRE_FALSE( ... ) (void)(0) ++ #define STATIC_CHECK( ... ) (void)(0) ++ #define STATIC_CHECK_FALSE( ... ) (void)(0) ++ ++ // "BDD-style" convenience wrappers ++ #define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ) ) ++ #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), className ) ++ ++ #define GIVEN( desc ) ++ #define AND_GIVEN( desc ) ++ #define WHEN( desc ) ++ #define AND_WHEN( desc ) ++ #define THEN( desc ) ++ #define AND_THEN( desc ) ++ ++#endif // ^^ unprefixed, disabled ++ ++// end of user facing macros ++ ++#endif // CATCH_TEST_MACROS_HPP_INCLUDED ++ ++ ++#ifndef CATCH_TEMPLATE_TEST_REGISTRY_HPP_INCLUDED ++#define CATCH_TEMPLATE_TEST_REGISTRY_HPP_INCLUDED ++ ++ ++ ++#ifndef CATCH_PREPROCESSOR_HPP_INCLUDED ++#define CATCH_PREPROCESSOR_HPP_INCLUDED ++ ++ ++#if defined(__GNUC__) ++// We need to silence "empty __VA_ARGS__ warning", and using just _Pragma does not work ++#pragma GCC system_header ++#endif ++ ++ ++#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ ++#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) ++#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) ++#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) ++#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) ++#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) ++ ++#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR ++#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ ++// MSVC needs more evaluations ++#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) ++#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) ++#else ++#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) ++#endif ++ ++#define CATCH_REC_END(...) ++#define CATCH_REC_OUT ++ ++#define CATCH_EMPTY() ++#define CATCH_DEFER(id) id CATCH_EMPTY() ++ ++#define CATCH_REC_GET_END2() 0, CATCH_REC_END ++#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 ++#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 ++#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT ++#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) ++#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) ++ ++#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) ++#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) ++#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) ++ ++#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) ++#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) ++#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) ++ ++// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, ++// and passes userdata as the first parameter to each invocation, ++// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) ++#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) ++ ++#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) ++ ++#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) ++#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR ++#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ ++#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) ++#else ++// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF ++#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) ++#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ ++#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) ++#endif ++ ++#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ ++#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) ++ ++#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR ++#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) ++#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) ++#else ++#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) ++#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) ++#endif ++ ++#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ ++ CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) ++ ++#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) ++#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) ++#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) ++#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) ++#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) ++#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) ++#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) ++#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) ++#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) ++#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) ++#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) ++ ++#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N ++ ++#define INTERNAL_CATCH_TYPE_GEN\ ++ template struct TypeList {};\ ++ template\ ++ constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ ++ template class...> struct TemplateTypeList{};\ ++ template class...Cs>\ ++ constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ ++ template\ ++ struct append;\ ++ template\ ++ struct rewrap;\ ++ template class, typename...>\ ++ struct create;\ ++ template class, typename>\ ++ struct convert;\ ++ \ ++ template \ ++ struct append { using type = T; };\ ++ template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ ++ struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ ++ template< template class L1, typename...E1, typename...Rest>\ ++ struct append, TypeList, Rest...> { using type = L1; };\ ++ \ ++ template< template class Container, template class List, typename...elems>\ ++ struct rewrap, List> { using type = TypeList>; };\ ++ template< template class Container, template class List, class...Elems, typename...Elements>\ ++ struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ ++ \ ++ template